UNPKG

@sphereon/ssi-sdk-ext.jwt-service

Version:

118 lines (104 loc) 4.51 kB
import { IAgentPlugin } from '@veramo/core' import debug from 'debug' import { importJWK } from 'jose' import * as u8a from 'uint8arrays' import { createJwsCompact, CreateJwsCompactArgs, CreateJwsFlattenedArgs, CreateJwsJsonArgs, createJwsJsonFlattened, createJwsJsonGeneral, DecryptJweCompactJwtArgs, EncryptJweCompactJwtArgs, IJwsValidationResult, IJwtService, IRequiredContext, jweAlg, jweEnc, JwsJsonFlattened, JwsJsonGeneral, JwtCompactResult, JwtLogger, PreparedJwsObject, prepareJwsObject, schema, verifyJws, VerifyJwsArgs, } from '..' import { CompactJwtEncrypter } from '../functions/JWE' /** * @public */ export class JwtService implements IAgentPlugin { readonly schema = schema.IJwtService readonly methods: IJwtService = { jwtPrepareJws: this.jwtPrepareJws.bind(this), jwtCreateJwsJsonGeneralSignature: this.jwtCreateJwsJsonGeneralSignature.bind(this), jwtCreateJwsJsonFlattenedSignature: this.jwtCreateJwsJsonFlattenedSignature.bind(this), jwtCreateJwsCompactSignature: this.jwtCreateJwsCompactSignature.bind(this), jwtVerifyJwsSignature: this.jwtVerifyJwsSignature.bind(this), jwtEncryptJweCompactJwt: this.jwtEncryptJweCompactJwt.bind(this), jwtDecryptJweCompactJwt: this.jwtDecryptJweCompactJwt.bind(this), } private async jwtPrepareJws(args: CreateJwsJsonArgs, context: IRequiredContext): Promise<PreparedJwsObject> { return await prepareJwsObject(args, context) } private async jwtCreateJwsJsonGeneralSignature(args: CreateJwsJsonArgs, context: IRequiredContext): Promise<JwsJsonGeneral> { return await createJwsJsonGeneral(args, context) } private async jwtCreateJwsJsonFlattenedSignature(args: CreateJwsFlattenedArgs, context: IRequiredContext): Promise<JwsJsonFlattened> { return await createJwsJsonFlattened(args, context) } private async jwtCreateJwsCompactSignature(args: CreateJwsCompactArgs, context: IRequiredContext): Promise<JwtCompactResult> { // We wrap it in a json object for remote REST calls return { jwt: await createJwsCompact(args, context) } } private async jwtVerifyJwsSignature(args: VerifyJwsArgs, context: IRequiredContext): Promise<IJwsValidationResult> { return await verifyJws(args, context) } private async jwtEncryptJweCompactJwt(args: EncryptJweCompactJwtArgs, context: IRequiredContext): Promise<JwtCompactResult> { const { payload, protectedHeader = { alg: args.alg, enc: args.enc }, recipientKey, issuer, expirationTime, audience } = args try { debug(`JWE Encrypt: ${JSON.stringify(args, null, 2)}`) const alg = jweAlg(args.alg) ?? jweAlg(protectedHeader.alg) ?? 'ECDH-ES' const enc = jweEnc(args.enc) ?? jweEnc(protectedHeader.enc) ?? 'A256GCM' const encJwks = recipientKey.jwks.length === 1 ? [recipientKey.jwks[0]] : recipientKey.jwks.filter((jwk) => (jwk.kid && (jwk.kid === jwk.jwk.kid || jwk.kid === jwk.jwkThumbprint)) || jwk.jwk.use === 'enc') if (encJwks.length === 0) { return Promise.reject(Error(`No public JWK found that can be used to encrypt against`)) } const jwkInfo = encJwks[0] if (encJwks.length > 0) { JwtLogger.warning(`More than one JWK with 'enc' usage found. Selected the first one as no 'kid' was provided`, encJwks) } if (jwkInfo.jwk.kty?.startsWith('EC') !== true || !alg.startsWith('ECDH')) { return Promise.reject(Error(`Currently only ECDH-ES is supported for encryption. JWK alg ${jwkInfo.jwk.kty}, header alg ${alg}`)) // TODO: Probably we support way more already } const apuVal = protectedHeader.apu ?? args.apu const apu = apuVal ? u8a.fromString(apuVal, 'base64url') : undefined const apvVal = protectedHeader.apv ?? args.apv const apv = apvVal ? u8a.fromString(apvVal, 'base64url') : undefined const pubKey = await importJWK(jwkInfo.jwk) const encrypter = new CompactJwtEncrypter({ enc, alg, keyManagementParams: { apu, apv }, key: pubKey, issuer, expirationTime, audience, }) const jwe = await encrypter.encryptCompactJWT(payload, {}) return { jwt: jwe } } catch (error: any) { console.error(`Error encrypting JWE: ${error.message}`, error) throw error } } private async jwtDecryptJweCompactJwt(args: DecryptJweCompactJwtArgs, context: IRequiredContext): Promise<JwtCompactResult> { return { jwt: 'FIXME' } } }