UNPKG

@sphereon/oid4vci-client

Version:

OpenID for Verifiable Credential Issuance (OpenID4VCI) client

196 lines (170 loc) • 7.21 kB
import { KeyObject } from 'crypto'; import { Alg, JWS_NOT_VALID, Jwt, NO_JWT_PROVIDED, OpenId4VCIVersion, PROOF_CANT_BE_CONSTRUCTED, ProofOfPossession } from '@sphereon/oid4vci-common'; import * as jose from 'jose'; import { ProofOfPossessionBuilder } from '..'; import { IDENTIPROOF_ISSUER_URL } from './MetadataMocks'; const jwt: Jwt = { header: { alg: Alg.ES256, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: 'JWT' }, payload: { iss: 'sphereon:wallet', nonce: 'tZignsnFbp', jti: 'tZignsnFbp223', aud: IDENTIPROOF_ISSUER_URL, iat: Date.now() / 1000 }, }; const jwt_withoutDid: Jwt = { header: { alg: Alg.ES256, kid: 'ebfeb1f712ebc6f1c276e12ec21/keys/1', typ: 'JWT' }, payload: { iss: 'sphereon:wallet', nonce: 'tZignsnFbp', jti: 'tZignsnFbp223', aud: IDENTIPROOF_ISSUER_URL, iat: Date.now() / 1000 }, }; const kid = 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1'; const kid_withoutDid = 'ebfeb1f712ebc6f1c276e12ec21/keys/1'; let keypair: KeyPair; async function proofOfPossessionCallbackFunction(args: Jwt, kid?: string): Promise<string> { if (!args.payload.aud) { throw Error('aud required'); } else if (!kid) { throw Error('kid required'); } return await new jose.SignJWT({ ...args.payload }) .setProtectedHeader({ alg: 'ES256' }) .setIssuedAt(args.payload.iat) .setIssuer(kid) .setAudience(args.payload.aud) .setExpirationTime('2h') .sign(keypair.privateKey); } interface KeyPair { publicKey: KeyObject; privateKey: KeyObject; } beforeAll(async () => { const { privateKey, publicKey } = await jose.generateKeyPair('ES256'); keypair = { publicKey: publicKey as KeyObject, privateKey: privateKey as KeyObject }; }); describe('ProofOfPossession Builder ', () => { it('should fail without supplied proof or callbacks', async function () { await expect( ProofOfPossessionBuilder.fromProof(undefined as never, OpenId4VCIVersion.VER_1_0_11) .withIssuer(IDENTIPROOF_ISSUER_URL) .withClientId('sphereon:wallet') .withKid(kid) .build(), ).rejects.toThrow(Error(PROOF_CANT_BE_CONSTRUCTED)); }); it('should fail without supplied proof or callbacks and with kid without did', async function () { await expect( ProofOfPossessionBuilder.fromProof(undefined as never, OpenId4VCIVersion.VER_1_0_13) .withIssuer(IDENTIPROOF_ISSUER_URL) .withClientId('sphereon:wallet') .withKid(kid_withoutDid) .build(), ).rejects.toThrow(Error(PROOF_CANT_BE_CONSTRUCTED)); }); it('should fail wit undefined jwt supplied', async function () { await expect(() => ProofOfPossessionBuilder.fromJwt({ jwt, callbacks: { signCallback: proofOfPossessionCallbackFunction }, version: OpenId4VCIVersion.VER_1_0_08 }) .withJwt(undefined as never) .withIssuer(IDENTIPROOF_ISSUER_URL) .withClientId('sphereon:wallet') .withKid(kid) .build(), ).toThrow(Error(NO_JWT_PROVIDED)); }); it('should fail with undefined jwt supplied and kid without did', async function () { await expect(() => ProofOfPossessionBuilder.fromJwt({ jwt: jwt_withoutDid, callbacks: { signCallback: proofOfPossessionCallbackFunction }, version: OpenId4VCIVersion.VER_1_0_08, }) .withJwt(undefined as never) .withIssuer(IDENTIPROOF_ISSUER_URL) .withClientId('sphereon:wallet') .withKid(kid_withoutDid) .build(), ).toThrow(Error(NO_JWT_PROVIDED)); }); it('should build a proof with all required params present', async function () { const proof: ProofOfPossession = await ProofOfPossessionBuilder.fromJwt({ jwt, callbacks: { signCallback: proofOfPossessionCallbackFunction, }, version: OpenId4VCIVersion.VER_1_0_08, }) .withIssuer(IDENTIPROOF_ISSUER_URL) .withKid(kid) .withClientId('sphereon:wallet') .build(); expect(proof).toBeDefined(); }); it('should build a proof with all required params present without did', async function () { const proof: ProofOfPossession = await ProofOfPossessionBuilder.fromJwt({ jwt: jwt_withoutDid, callbacks: { signCallback: proofOfPossessionCallbackFunction, }, version: OpenId4VCIVersion.VER_1_0_08, }) .withIssuer(IDENTIPROOF_ISSUER_URL) .withKid(kid_withoutDid) .withClientId('sphereon:wallet') .build(); expect(proof).toBeDefined(); }); it('should fail creating a proof of possession with simple verification', async () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars async function proofOfPossessionCallbackFunction(_args: Jwt, _kid?: string): Promise<string> { throw new Error(JWS_NOT_VALID); } await expect( ProofOfPossessionBuilder.fromJwt({ jwt, callbacks: { signCallback: proofOfPossessionCallbackFunction }, version: OpenId4VCIVersion.VER_1_0_08 }) .withIssuer(IDENTIPROOF_ISSUER_URL) .withClientId('sphereon:wallet') .withKid(kid) .build(), ).rejects.toThrow(Error(JWS_NOT_VALID)); }); it('should fail creating a proof of possession with simple verification and without did', async () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars async function proofOfPossessionCallbackFunction(_args: Jwt, _kid?: string): Promise<string> { throw new Error(JWS_NOT_VALID); } await expect( ProofOfPossessionBuilder.fromJwt({ jwt: jwt_withoutDid, callbacks: { signCallback: proofOfPossessionCallbackFunction }, version: OpenId4VCIVersion.VER_1_0_08, }) .withIssuer(IDENTIPROOF_ISSUER_URL) .withClientId('sphereon:wallet') .withKid(kid_withoutDid) .build(), ).rejects.toThrow(Error(JWS_NOT_VALID)); }); it('should fail creating a proof of possession without verify callback', async () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars async function proofOfPossessionCallbackFunction(_args: Jwt, _kid?: string): Promise<string> { throw new Error(JWS_NOT_VALID); } await expect( ProofOfPossessionBuilder.fromJwt({ jwt, callbacks: { signCallback: proofOfPossessionCallbackFunction }, version: OpenId4VCIVersion.VER_1_0_08 }) .withIssuer(IDENTIPROOF_ISSUER_URL) .withClientId('sphereon:wallet') .withKid(kid) .build(), ).rejects.toThrow(Error(JWS_NOT_VALID)); }); it('should fail creating a proof of possession without verify callback and without did', async () => { // eslint-disable-next-line @typescript-eslint/no-unused-vars async function proofOfPossessionCallbackFunction(_args: Jwt, _kid?: string): Promise<string> { throw new Error(JWS_NOT_VALID); } await expect( ProofOfPossessionBuilder.fromJwt({ jwt: jwt_withoutDid, callbacks: { signCallback: proofOfPossessionCallbackFunction }, version: OpenId4VCIVersion.VER_1_0_08, }) .withIssuer(IDENTIPROOF_ISSUER_URL) .withClientId('sphereon:wallet') .withKid(kid_withoutDid) .build(), ).rejects.toThrow(Error(JWS_NOT_VALID)); }); });