From 951bf2cb20d0a2a085a8a346d1ed519c71e31a07 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Fri, 17 Nov 2023 17:10:06 +0700 Subject: [PATCH 01/11] feat: add sd-jwt issuer support and e2e test Signed-off-by: Timo Glastra --- packages/client/lib/OpenID4VCIClient.ts | 26 ++- .../client/lib/ProofOfPossessionBuilder.ts | 8 + packages/client/lib/__tests__/SdJwt.spec.ts | 157 ++++++++++++++++++ packages/client/lib/functions/ProofUtil.ts | 22 ++- packages/issuer/lib/VcIssuer.ts | 2 +- 5 files changed, 203 insertions(+), 12 deletions(-) create mode 100644 packages/client/lib/__tests__/SdJwt.spec.ts diff --git a/packages/client/lib/OpenID4VCIClient.ts b/packages/client/lib/OpenID4VCIClient.ts index 227eb835..f0548293 100644 --- a/packages/client/lib/OpenID4VCIClient.ts +++ b/packages/client/lib/OpenID4VCIClient.ts @@ -9,6 +9,8 @@ import { CredentialSupported, EndpointMetadataResult, JsonURIMode, + JWK, + KID_JWK_X5C_ERROR, OID4VCICredentialFormat, OpenId4VCIVersion, ProofOfPossessionCallbacks, @@ -49,6 +51,7 @@ export class OpenID4VCIClient { private readonly _credentialOffer: CredentialOfferRequestWithBaseUrl; private _clientId?: string; private _kid: string | undefined; + private _jwk: JWK | undefined; private _alg: Alg | string | undefined; private _endpointMetadata: EndpointMetadataResult | undefined; private _accessTokenResponse: AccessTokenResponse | undefined; @@ -281,6 +284,7 @@ export class OpenID4VCIClient { proofCallbacks, format, kid, + jwk, alg, jti, }: { @@ -288,16 +292,18 @@ export class OpenID4VCIClient { proofCallbacks: ProofOfPossessionCallbacks; format?: CredentialFormat | OID4VCICredentialFormat; kid?: string; + jwk?: JWK; alg?: Alg | string; jti?: string; }): Promise { - if (alg) { - this._alg = alg; - } - if (kid) { - this._kid = kid; + if ([jwk, kid].filter((v) => v !== undefined).length > 1) { + throw new Error(KID_JWK_X5C_ERROR + `. jwk: ${jwk !== undefined}, kid: ${kid !== undefined}`); } + if (alg) this._alg = alg; + if (jwk) this._jwk = jwk; + if (kid) this._kid = kid; + const requestBuilder = CredentialRequestClientBuilder.fromCredentialOffer({ credentialOffer: this.credentialOffer, metadata: this.endpointMetadata, @@ -339,8 +345,14 @@ export class OpenID4VCIClient { version: this.version(), }) .withIssuer(this.getIssuer()) - .withAlg(this.alg) - .withKid(this.kid); + .withAlg(this.alg); + + if (this._jwk) { + proofBuilder.withJWK(this._jwk); + } + if (this._kid) { + proofBuilder.withKid(this._kid); + } if (this.clientId) { proofBuilder.withClientId(this.clientId); diff --git a/packages/client/lib/ProofOfPossessionBuilder.ts b/packages/client/lib/ProofOfPossessionBuilder.ts index ac402cf0..8aef307f 100644 --- a/packages/client/lib/ProofOfPossessionBuilder.ts +++ b/packages/client/lib/ProofOfPossessionBuilder.ts @@ -2,6 +2,7 @@ import { AccessTokenResponse, Alg, EndpointMetadata, + JWK, Jwt, NO_JWT_PROVIDED, OpenId4VCIVersion, @@ -19,6 +20,7 @@ export class ProofOfPossessionBuilder { private readonly version: OpenId4VCIVersion; private kid?: string; + private jwk?: JWK; private clientId?: string; private issuer?: string; private jwt?: Jwt; @@ -91,6 +93,11 @@ export class ProofOfPossessionBuilder { return this; } + withJWK(jwk: JWK): this { + this.jwk = jwk; + return this; + } + withIssuer(issuer: string): this { this.issuer = issuer; return this; @@ -182,6 +189,7 @@ export class ProofOfPossessionBuilder { { typ: this.typ ?? (this.version < OpenId4VCIVersion.VER_1_0_11 ? 'jwt' : 'openid4vci-proof+jwt'), kid: this.kid, + jwk: this.jwk, jti: this.jti, alg: this.alg, issuer: this.issuer, diff --git a/packages/client/lib/__tests__/SdJwt.spec.ts b/packages/client/lib/__tests__/SdJwt.spec.ts new file mode 100644 index 00000000..81eebecf --- /dev/null +++ b/packages/client/lib/__tests__/SdJwt.spec.ts @@ -0,0 +1,157 @@ +import { AccessTokenRequest, CredentialRequestV1_0_11, CredentialSupportedSdJwtVc } from '@sphereon/oid4vci-common'; +import nock from 'nock'; + +import { OpenID4VCIClient } from '..'; +import { createAccessTokenResponse, IssuerMetadataBuilderV1_11, VcIssuerBuilder } from '../../../issuer'; + +export const UNIT_TEST_TIMEOUT = 30000; + +const alg = 'ES256'; +const jwk = { kty: 'EC', crv: 'P-256', x: 'zQOowIC1gWJtdddB5GAt4lau6Lt8Ihy771iAfam-1pc', y: 'cjD_7o3gdQ1vgiQy3_sMGs7WrwCMU9FQYimA3HxnMlw' }; + +const issuerMetadata = new IssuerMetadataBuilderV1_11() + .withCredentialIssuer('https://example.com') + .withCredentialEndpoint('https://credenital-endpoint.example.com') + .withTokenEndpoint('https://token-endpoint.example.com') + .addSupportedCredential({ + format: 'vc+sd-jwt', + credential_definition: { + vct: 'SdJwtCredential', + }, + id: 'SdJwtCredentialId', + }) + .build(); + +const vcIssuer = new VcIssuerBuilder() + .withIssuerMetadata(issuerMetadata) + .withInMemoryCNonceState() + .withInMemoryCredentialOfferState() + .withInMemoryCredentialOfferURIState() + // TODO: see if we can construct an sd-jwt vc based on the input + .withCredentialSignerCallback(async () => { + return 'sd-jwt'; + }) + .withJWTVerifyCallback(() => + Promise.resolve({ + alg, + jwk, + jwt: { + header: { + typ: 'openid4vci-proof+jwt', + alg, + jwk, + }, + payload: { + aud: issuerMetadata.credential_issuer, + iat: +new Date(), + nonce: 'a-c-nonce', + }, + }, + }), + ) + .build(); + +describe('sd-jwt vc', () => { + beforeEach(() => { + nock.cleanAll(); + }); + afterEach(() => { + nock.cleanAll(); + }); + + it( + 'succeed with a full flow', + async () => { + const offerUri = await vcIssuer.createCredentialOfferURI({ + grants: { + 'urn:ietf:params:oauth:grant-type:pre-authorized_code': { + 'pre-authorized_code': '123', + user_pin_required: false, + }, + }, + credentials: ['SdJwtCredentialId'], + }); + + nock(vcIssuer.issuerMetadata.credential_issuer).get('/.well-known/openid-credential-issuer').reply(200, JSON.stringify(issuerMetadata)); + nock(vcIssuer.issuerMetadata.credential_issuer).get('/.well-known/openid-configuration').reply(404); + nock(vcIssuer.issuerMetadata.credential_issuer).get('/.well-known/oauth-authorization-server').reply(404); + + expect(offerUri.uri).toEqual( + 'openid-credential-offer://?credential_offer=%7B%22grants%22%3A%7B%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22123%22%2C%22user_pin_required%22%3Afalse%7D%7D%2C%22credentials%22%3A%5B%22SdJwtCredentialId%22%5D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fexample.com%22%7D', + ); + + const client = await OpenID4VCIClient.fromURI({ + uri: offerUri.uri, + }); + + expect(client.credentialOffer.credential_offer).toEqual({ + credential_issuer: 'https://example.com', + credentials: ['SdJwtCredentialId'], + grants: { + 'urn:ietf:params:oauth:grant-type:pre-authorized_code': { + 'pre-authorized_code': '123', + user_pin_required: false, + }, + }, + }); + + const supported = client.getCredentialsSupported(true, 'vc+sd-jwt'); + expect(supported).toEqual([ + { + credential_definition: { + vct: 'SdJwtCredential', + }, + format: 'vc+sd-jwt', + id: 'SdJwtCredentialId', + }, + ]); + + const offered = supported[0] as CredentialSupportedSdJwtVc; + + nock(issuerMetadata.token_endpoint as string) + .post('/') + .reply(200, async (_, body: string) => { + const parsedBody = Object.fromEntries(body.split('&').map((x) => x.split('='))); + return createAccessTokenResponse(parsedBody as AccessTokenRequest, { + credentialOfferSessions: vcIssuer.credentialOfferSessions, + accessTokenIssuer: 'https://issuer.example.com', + cNonces: vcIssuer.cNonces, + cNonce: 'a-c-nonce', + accessTokenSignerCallback: async () => 'ey.val.ue', + tokenExpiresIn: 500, + }); + }); + + await client.acquireAccessToken({}); + + nock(issuerMetadata.credential_endpoint as string) + .post('/') + .reply(200, async (_, body) => + vcIssuer.issueCredential({ + credentialRequest: body as CredentialRequestV1_0_11, + credential: {} as any, // TODO: define the interface for credential when using sd-jwt + newCNonce: 'new-c-nonce', + }), + ); + + const credentials = await client.acquireCredentials({ + credentialTypes: [offered.credential_definition.vct], + format: 'vc+sd-jwt', + alg, + jwk, + proofCallbacks: { + // When using sd-jwt for real, this jwt should include a jwk + signCallback: async () => 'ey.ja.ja', + }, + }); + + expect(credentials).toEqual({ + c_nonce: 'new-c-nonce', + c_nonce_expires_in: 300000, + credential: 'sd-jwt', // TODO: make this a real sd-jwt vc + format: 'vc+sd-jwt', + }); + }, + UNIT_TEST_TIMEOUT, + ); +}); diff --git a/packages/client/lib/functions/ProofUtil.ts b/packages/client/lib/functions/ProofUtil.ts index 53fe90b6..cbfed4be 100644 --- a/packages/client/lib/functions/ProofUtil.ts +++ b/packages/client/lib/functions/ProofUtil.ts @@ -1,4 +1,15 @@ -import { BAD_PARAMS, JWS_NOT_VALID, Jwt, JWTHeader, JWTPayload, ProofOfPossession, ProofOfPossessionCallbacks, Typ } from '@sphereon/oid4vci-common'; +import { + BAD_PARAMS, + BaseJWK, + JWK, + JWS_NOT_VALID, + Jwt, + JWTHeader, + JWTPayload, + ProofOfPossession, + ProofOfPossessionCallbacks, + Typ, +} from '@sphereon/oid4vci-common'; import Debug from 'debug'; const debug = Debug('sphereon:openid4vci:token'); @@ -61,6 +72,7 @@ const partiallyValidateJWS = (jws: string): void => { export interface JwtProps { typ?: Typ; kid?: string; + jwk?: JWK; issuer?: string; clientId?: string; alg?: string; @@ -76,7 +88,8 @@ const createJWT = (jwtProps?: JwtProps, existingJwt?: Jwt): Jwt => { const nonce = getJwtProperty('nonce', false, jwtProps?.nonce, existingJwt?.payload?.nonce); // Officially this is required, but some implementations don't have it // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const alg = getJwtProperty('alg', false, jwtProps?.alg, existingJwt?.header?.alg, 'ES256')!; - const kid = getJwtProperty('kid', true, jwtProps?.kid, existingJwt?.header?.kid); + const kid = getJwtProperty('kid', false, jwtProps?.kid, existingJwt?.header?.kid); + const jwk = getJwtProperty('jwk', false, jwtProps?.jwk, existingJwt?.header?.jwk); const jwt: Partial = existingJwt ? existingJwt : {}; const now = +new Date(); const jwtPayload: Partial = { @@ -92,6 +105,7 @@ const createJWT = (jwtProps?: JwtProps, existingJwt?: Jwt): Jwt => { typ, alg, kid, + jwk, }; return { payload: { ...jwt.payload, ...jwtPayload }, @@ -99,8 +113,8 @@ const createJWT = (jwtProps?: JwtProps, existingJwt?: Jwt): Jwt => { }; }; -const getJwtProperty = (propertyName: string, required: boolean, option?: string, jwtProperty?: T, defaultValue?: T): T | undefined => { - if (option && jwtProperty && option !== jwtProperty) { +const getJwtProperty = (propertyName: string, required: boolean, option?: string | JWK, jwtProperty?: T, defaultValue?: T): T | undefined => { + if (typeof option === 'string' && option && jwtProperty && option !== jwtProperty) { throw Error(`Cannot have a property '${propertyName}' with value '${option}' and different JWT value '${jwtProperty}' at the same time`); } let result = (jwtProperty ? jwtProperty : option) as T | undefined; diff --git a/packages/issuer/lib/VcIssuer.ts b/packages/issuer/lib/VcIssuer.ts index b8411f07..5fa1679d 100644 --- a/packages/issuer/lib/VcIssuer.ts +++ b/packages/issuer/lib/VcIssuer.ts @@ -404,7 +404,7 @@ export class VcIssuer { let preAuthorizedCode: string | undefined let issuerState: string | undefined try { - if (credentialRequest.format !== 'jwt_vc_json' && credentialRequest.format !== 'jwt_vc_json-ld') { + if (credentialRequest.format !== 'jwt_vc_json' && credentialRequest.format !== 'jwt_vc_json-ld' && credentialRequest.format !== 'vc+sd-jwt') { throw Error(`Format ${credentialRequest.format} not supported yet`) } else if (typeof this._jwtVerifyCallback !== 'function' && typeof jwtVerifyCallback !== 'function') { throw new Error(JWT_VERIFY_CONFIG_ERROR) From 0d928ccc1b7c35564e9a7e225e31a282c04eba15 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Thu, 7 Dec 2023 16:50:51 +0700 Subject: [PATCH 02/11] update with correct typing Signed-off-by: Timo Glastra --- .../callback-example/lib/IssuerCallback.ts | 7 +++-- .../lib/__tests__/issuerCallback.spec.ts | 2 +- packages/client/lib/__tests__/SdJwt.spec.ts | 10 ++++++- packages/issuer/lib/VcIssuer.ts | 30 +++++++++++-------- packages/issuer/lib/types/index.ts | 8 +++-- 5 files changed, 36 insertions(+), 21 deletions(-) diff --git a/packages/callback-example/lib/IssuerCallback.ts b/packages/callback-example/lib/IssuerCallback.ts index 4319f3e0..6e634b38 100644 --- a/packages/callback-example/lib/IssuerCallback.ts +++ b/packages/callback-example/lib/IssuerCallback.ts @@ -4,7 +4,8 @@ import { Ed25519VerificationKey2020 } from '@digitalcredentials/ed25519-verifica import { securityLoader } from '@digitalcredentials/security-document-loader' import vc from '@digitalcredentials/vc' import { CredentialRequestV1_0_11 } from '@sphereon/oid4vci-common' -import { ICredential, W3CVerifiableCredential } from '@sphereon/ssi-types' +import { CredentialIssuanceInput } from '@sphereon/oid4vci-issuer' +import { W3CVerifiableCredential } from '@sphereon/ssi-types' // Example on how to generate a did:key to issue a verifiable credential export const generateDid = async () => { @@ -14,12 +15,12 @@ export const generateDid = async () => { } // eslint-disable-next-line @typescript-eslint/no-explicit-any -export const getIssuerCallback = (credential: ICredential, keyPair: any, verificationMethod: string) => { +export const getIssuerCallback = (credential: CredentialIssuanceInput, keyPair: any, verificationMethod: string) => { if (!credential) { throw new Error('A credential needs to be provided') } // eslint-disable-next-line @typescript-eslint/no-unused-vars - return async (_opts: { credentialRequest?: CredentialRequestV1_0_11; credential?: ICredential }): Promise => { + return async (_opts: { credentialRequest?: CredentialRequestV1_0_11; credential?: CredentialIssuanceInput }): Promise => { const documentLoader = securityLoader().build() // eslint-disable-next-line @typescript-eslint/no-explicit-any const verificationKey: any = Array.from(keyPair.values())[0] diff --git a/packages/callback-example/lib/__tests__/issuerCallback.spec.ts b/packages/callback-example/lib/__tests__/issuerCallback.spec.ts index 16ae66d2..9ec8f93a 100644 --- a/packages/callback-example/lib/__tests__/issuerCallback.spec.ts +++ b/packages/callback-example/lib/__tests__/issuerCallback.spec.ts @@ -167,7 +167,7 @@ describe('issuerCallback', () => { ) .withCredentialSignerCallback((opts) => Promise.resolve({ - ...opts.credential, + ...(opts.credential as ICredential), proof: { type: IProofType.JwtProof2020, jwt: 'ye.ye.ye', diff --git a/packages/client/lib/__tests__/SdJwt.spec.ts b/packages/client/lib/__tests__/SdJwt.spec.ts index 81eebecf..3a9c8804 100644 --- a/packages/client/lib/__tests__/SdJwt.spec.ts +++ b/packages/client/lib/__tests__/SdJwt.spec.ts @@ -129,7 +129,15 @@ describe('sd-jwt vc', () => { .reply(200, async (_, body) => vcIssuer.issueCredential({ credentialRequest: body as CredentialRequestV1_0_11, - credential: {} as any, // TODO: define the interface for credential when using sd-jwt + credential: { + vct: 'Hello', + iss: 'did:example:123', + iat: 123, + // Defines what can be disclosed (optional) + __disclosureFrame: { + name: true, + }, + }, newCNonce: 'new-c-nonce', }), ); diff --git a/packages/issuer/lib/VcIssuer.ts b/packages/issuer/lib/VcIssuer.ts index 5fa1679d..5eb92133 100644 --- a/packages/issuer/lib/VcIssuer.ts +++ b/packages/issuer/lib/VcIssuer.ts @@ -36,12 +36,12 @@ import { UniformCredentialRequest, URIState, } from '@sphereon/oid4vci-common' -import { ICredential, W3CVerifiableCredential } from '@sphereon/ssi-types' +import { CredentialMapper, W3CVerifiableCredential } from '@sphereon/ssi-types' import { v4 } from 'uuid' import { assertValidPinNumber, createCredentialOfferObject, createCredentialOfferURIFromObject } from './functions' import { LookupStateManager } from './state-manager' -import { CredentialDataSupplier, CredentialDataSupplierArgs, CredentialSignerCallback } from './types' +import { CredentialDataSupplier, CredentialDataSupplierArgs, CredentialIssuanceInput, CredentialSignerCallback } from './types' const SECOND = 1000 @@ -219,7 +219,7 @@ export class VcIssuer { */ public async issueCredential(opts: { credentialRequest: CredentialRequestV1_0_11 - credential?: ICredential + credential?: CredentialIssuanceInput credentialDataSupplier?: CredentialDataSupplier credentialDataSupplierInput?: CredentialDataSupplierInput newCNonce?: string @@ -255,7 +255,7 @@ export class VcIssuer { if (!opts.credential && this._credentialDataSupplier === undefined && opts.credentialDataSupplier === undefined) { throw Error(`Either a credential needs to be supplied or a credentialDataSupplier`) } - let credential: ICredential | undefined + let credential: CredentialIssuanceInput | undefined let format: OID4VCICredentialFormat = opts.credentialRequest.format let signerCallback: CredentialSignerCallback | undefined = opts.credentialSignerCallback if (opts.credential) { @@ -295,14 +295,18 @@ export class VcIssuer { throw Error('A credential needs to be supplied at this point') } if (did) { - const credentialSubjects = Array.isArray(credential.credentialSubject) ? credential.credentialSubject : [credential.credentialSubject] - credentialSubjects.map((subject) => { - if (!subject.id) { - subject.id = did - } - return subject - }) - credential.credentialSubject = Array.isArray(credential.credentialSubject) ? credentialSubjects : credentialSubjects[0] + if (CredentialMapper.isSdJwtDecodedCredentialPayload(credential)) { + credential.sub = did + } else { + const credentialSubjects = Array.isArray(credential.credentialSubject) ? credential.credentialSubject : [credential.credentialSubject] + credentialSubjects.map((subject) => { + if (!subject.id) { + subject.id = did + } + return subject + }) + credential.credentialSubject = Array.isArray(credential.credentialSubject) ? credentialSubjects : credentialSubjects[0] + } } const verifiableCredential = await this.issueCredentialImpl( @@ -526,7 +530,7 @@ export class VcIssuer { private async issueCredentialImpl( opts: { credentialRequest: UniformCredentialRequest - credential: ICredential + credential: CredentialIssuanceInput jwtVerifyResult: JwtVerifyResult format?: OID4VCICredentialFormat }, diff --git a/packages/issuer/lib/types/index.ts b/packages/issuer/lib/types/index.ts index 71fc1ba3..438797ca 100644 --- a/packages/issuer/lib/types/index.ts +++ b/packages/issuer/lib/types/index.ts @@ -7,11 +7,11 @@ import { OID4VCICredentialFormat, UniformCredentialRequest, } from '@sphereon/oid4vci-common' -import { ICredential, W3CVerifiableCredential } from '@sphereon/ssi-types' +import { ICredential, SdJwtDecodedVerifiableCredentialPayload, SdJwtDisclosureFrame, W3CVerifiableCredential } from '@sphereon/ssi-types' export type CredentialSignerCallback = (opts: { credentialRequest: UniformCredentialRequest - credential: ICredential + credential: CredentialIssuanceInput format?: OID4VCICredentialFormat /** * We use object since we don't want to expose the DID Document TS type to too many interfaces. @@ -28,8 +28,10 @@ export interface CredentialDataSupplierArgs extends CNonceState { credentialDataSupplierInput?: CredentialDataSupplierInput } +export type CredentialIssuanceInput = ICredential | (SdJwtDecodedVerifiableCredentialPayload & { __disclosureFrame?: SdJwtDisclosureFrame }) + export interface CredentialDataSupplierResult { - credential: ICredential + credential: CredentialIssuanceInput format?: OID4VCICredentialFormat signCallback?: CredentialSignerCallback // If the data supplier wants to actually sign directly } From 74d2d92498298d122fce14fcbdbc8b63c042b86d Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Mon, 18 Dec 2023 17:14:39 +0700 Subject: [PATCH 03/11] update sd-jwt profile from oid4vci Signed-off-by: Timo Glastra --- .../client/lib/CredentialRequestClient.ts | 4 +--- packages/client/lib/OpenID4VCIClient.ts | 4 ++-- packages/client/lib/__tests__/SdJwt.spec.ts | 12 ++++------- .../lib/functions/CredentialOfferUtil.ts | 2 +- .../lib/functions/CredentialRequestUtil.ts | 2 +- .../lib/functions/IssuerMetadataUtils.ts | 2 +- .../common/lib/types/Authorization.types.ts | 4 ++-- packages/common/lib/types/Generic.types.ts | 20 ++++++++++--------- .../CredentialSupportedBuilderV1_11.ts | 4 +--- 9 files changed, 24 insertions(+), 30 deletions(-) diff --git a/packages/client/lib/CredentialRequestClient.ts b/packages/client/lib/CredentialRequestClient.ts index 8bcdfb89..f4f3508d 100644 --- a/packages/client/lib/CredentialRequestClient.ts +++ b/packages/client/lib/CredentialRequestClient.ts @@ -123,9 +123,7 @@ export class CredentialRequestClient { return { format, proof, - credential_definition: { - vct: types[0], - }, + vct: types[0], }; } diff --git a/packages/client/lib/OpenID4VCIClient.ts b/packages/client/lib/OpenID4VCIClient.ts index f0548293..377b638a 100644 --- a/packages/client/lib/OpenID4VCIClient.ts +++ b/packages/client/lib/OpenID4VCIClient.ts @@ -411,8 +411,8 @@ export class OpenID4VCIClient { return [c]; } else if ('types' in c) { return c.types; - } else if ('vct' in c.credential_definition) { - return [c.credential_definition.vct]; + } else if ('vct' in c) { + return [c.vct]; } else { return c.credential_definition.types; } diff --git a/packages/client/lib/__tests__/SdJwt.spec.ts b/packages/client/lib/__tests__/SdJwt.spec.ts index 3a9c8804..e3f8311d 100644 --- a/packages/client/lib/__tests__/SdJwt.spec.ts +++ b/packages/client/lib/__tests__/SdJwt.spec.ts @@ -15,9 +15,7 @@ const issuerMetadata = new IssuerMetadataBuilderV1_11() .withTokenEndpoint('https://token-endpoint.example.com') .addSupportedCredential({ format: 'vc+sd-jwt', - credential_definition: { - vct: 'SdJwtCredential', - }, + vct: 'SdJwtCredential', id: 'SdJwtCredentialId', }) .build(); @@ -98,9 +96,7 @@ describe('sd-jwt vc', () => { const supported = client.getCredentialsSupported(true, 'vc+sd-jwt'); expect(supported).toEqual([ { - credential_definition: { - vct: 'SdJwtCredential', - }, + vct: 'SdJwtCredential', format: 'vc+sd-jwt', id: 'SdJwtCredentialId', }, @@ -143,7 +139,7 @@ describe('sd-jwt vc', () => { ); const credentials = await client.acquireCredentials({ - credentialTypes: [offered.credential_definition.vct], + credentialTypes: [offered.vct], format: 'vc+sd-jwt', alg, jwk, @@ -156,7 +152,7 @@ describe('sd-jwt vc', () => { expect(credentials).toEqual({ c_nonce: 'new-c-nonce', c_nonce_expires_in: 300000, - credential: 'sd-jwt', // TODO: make this a real sd-jwt vc + credential: 'sd-jwt', format: 'vc+sd-jwt', }); }, diff --git a/packages/common/lib/functions/CredentialOfferUtil.ts b/packages/common/lib/functions/CredentialOfferUtil.ts index 7f2428f1..df56e116 100644 --- a/packages/common/lib/functions/CredentialOfferUtil.ts +++ b/packages/common/lib/functions/CredentialOfferUtil.ts @@ -353,7 +353,7 @@ export function getTypesFromOffer(credentialOffer: UniformCredentialOfferPayload } else if (curr.format === 'jwt_vc_json') { return [...prev, ...curr.types]; } else if (curr.format === 'vc+sd-jwt') { - return [...prev, curr.credential_definition.vct]; + return [...prev, curr.vct]; } return prev; diff --git a/packages/common/lib/functions/CredentialRequestUtil.ts b/packages/common/lib/functions/CredentialRequestUtil.ts index 66ee9fe6..854ca3b6 100644 --- a/packages/common/lib/functions/CredentialRequestUtil.ts +++ b/packages/common/lib/functions/CredentialRequestUtil.ts @@ -9,7 +9,7 @@ export function getTypesFromRequest(credentialRequest: UniformCredentialRequest, } else if (credentialRequest.format === 'jwt_vc_json-ld' || credentialRequest.format === 'ldp_vc') { types = credentialRequest.credential_definition.types; } else if (credentialRequest.format === 'vc+sd-jwt') { - types = [credentialRequest.credential_definition.vct]; + types = [credentialRequest.vct]; } if (!types || types.length === 0) { diff --git a/packages/common/lib/functions/IssuerMetadataUtils.ts b/packages/common/lib/functions/IssuerMetadataUtils.ts index 40eb85d6..d031974d 100644 --- a/packages/common/lib/functions/IssuerMetadataUtils.ts +++ b/packages/common/lib/functions/IssuerMetadataUtils.ts @@ -104,7 +104,7 @@ export function getTypesFromCredentialSupported(credentialSupported: CredentialS if (credentialSupported.format === 'jwt_vc_json' || credentialSupported.format === 'jwt_vc_json-ld' || credentialSupported.format === 'ldp_vc') { types = credentialSupported.types; } else if (credentialSupported.format === 'vc+sd-jwt') { - types = [credentialSupported.credential_definition.vct]; + types = [credentialSupported.vct]; } if (!types || types.length === 0) { diff --git a/packages/common/lib/types/Authorization.types.ts b/packages/common/lib/types/Authorization.types.ts index 0276d510..895eef20 100644 --- a/packages/common/lib/types/Authorization.types.ts +++ b/packages/common/lib/types/Authorization.types.ts @@ -5,7 +5,6 @@ import { JsonLdIssuerCredentialDefinition, OID4VCICredentialFormat, PRE_AUTH_CODE_LITERAL, - SdJwtVcCredentialDefinition, } from './Generic.types'; import { EndpointMetadata } from './ServerMetadata'; @@ -140,7 +139,8 @@ export interface AuthorizationDetailsJwtVcJsonLdAndLdpVc extends CommonAuthoriza export interface AuthorizationDetailsSdJwtVc extends CommonAuthorizationDetails { format: 'vc+sd-jwt'; - credential_definition: SdJwtVcCredentialDefinition; + vct: string; + claims?: IssuerCredentialSubject; } export enum GrantTypes { diff --git a/packages/common/lib/types/Generic.types.ts b/packages/common/lib/types/Generic.types.ts index 16e042df..28a75909 100644 --- a/packages/common/lib/types/Generic.types.ts +++ b/packages/common/lib/types/Generic.types.ts @@ -90,16 +90,12 @@ export interface CredentialSupportedJwtVcJson extends CommonCredentialSupported format: 'jwt_vc_json'; } -export interface SdJwtVcCredentialDefinition { - vct: string; // REQUIRED. JSON string designating the type of an SD-JWT vc - claims?: IssuerCredentialSubject; -} - export interface CredentialSupportedSdJwtVc extends CommonCredentialSupported { format: 'vc+sd-jwt'; - // REQUIRED. JSON object containing the detailed description of the credential type - credential_definition: SdJwtVcCredentialDefinition; + vct: string; + claims?: IssuerCredentialSubject; + order?: string[]; //An array of claims.display.name values that lists them in the order they should be displayed by the Wallet. } @@ -121,9 +117,14 @@ export interface CredentialOfferFormatJwtVcJson extends CommonCredentialOfferFor types: string[]; // REQUIRED. JSON array as defined in Appendix E.1.1.2. This claim contains the type values the Wallet shall request in the subsequent Credential Request. } +// NOTE: the sd-jwt format is added to oid4vci in a later draft version than currently +// supported, so there's no defined offer format. However, based on the request structure +// we support sd-jwt for older drafts of oid4vci as well export interface CredentialOfferFormatSdJwtVc extends CommonCredentialOfferFormat { format: 'vc+sd-jwt'; - credential_definition: SdJwtVcCredentialDefinition; + + vct: string; + claims?: IssuerCredentialSubject; } export type CredentialOfferFormat = CommonCredentialOfferFormat & @@ -176,7 +177,8 @@ export interface CredentialRequestJwtVcJsonLdAndLdpVc extends CommonCredentialRe export interface CredentialRequestSdJwtVc extends CommonCredentialRequest { format: 'vc+sd-jwt'; - credential_definition: SdJwtVcCredentialDefinition; + vct: string; + claims?: IssuerCredentialSubject; } export interface CommonCredentialResponse { diff --git a/packages/issuer/lib/builder/CredentialSupportedBuilderV1_11.ts b/packages/issuer/lib/builder/CredentialSupportedBuilderV1_11.ts index 1ba45598..ab2ecafa 100644 --- a/packages/issuer/lib/builder/CredentialSupportedBuilderV1_11.ts +++ b/packages/issuer/lib/builder/CredentialSupportedBuilderV1_11.ts @@ -125,9 +125,7 @@ export class CredentialSupportedBuilderV1_11 { if (this.types.length > 1) { throw new Error('Only one type is allowed for vc+sd-jwt') } - credentialSupported.credential_definition = { - vct: this.types[0], - } + credentialSupported.vct = this.types[0] } // And else would work here, but this way we get the correct typing else if (isNotFormat(credentialSupported, 'vc+sd-jwt')) { From 13659a7b82789cc0f011d3c056ced13b3cbed290 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Thu, 21 Dec 2023 19:07:03 +0100 Subject: [PATCH 04/11] feat: Add initial support for creating a client without credential offer --- packages/client/lib/OpenID4VCIClient.ts | 139 +++++++++++++++++++----- 1 file changed, 110 insertions(+), 29 deletions(-) diff --git a/packages/client/lib/OpenID4VCIClient.ts b/packages/client/lib/OpenID4VCIClient.ts index 227eb835..d13e8040 100644 --- a/packages/client/lib/OpenID4VCIClient.ts +++ b/packages/client/lib/OpenID4VCIClient.ts @@ -8,6 +8,9 @@ import { CredentialResponse, CredentialSupported, EndpointMetadataResult, + getIssuerFromCredentialOfferPayload, + getSupportedCredentials, + getTypesFromCredentialSupported, JsonURIMode, OID4VCICredentialFormat, OpenId4VCIVersion, @@ -15,8 +18,6 @@ import { PushedAuthorizationResponse, ResponseType, } from '@sphereon/oid4vci-common'; -import { getSupportedCredentials, getTypesFromCredentialSupported } from '@sphereon/oid4vci-common/dist/functions/IssuerMetadataUtils'; -import { CredentialSupportedTypeV1_0_08 } from '@sphereon/oid4vci-common/dist/types/v1_0_08.types'; import { CredentialFormat } from '@sphereon/ssi-types'; import Debug from 'debug'; @@ -39,27 +40,66 @@ interface AuthDetails { interface AuthRequestOpts { codeChallenge: string; - codeChallengeMethod: CodeChallengeMethod; + codeChallengeMethod?: CodeChallengeMethod; authorizationDetails?: AuthDetails | AuthDetails[]; redirectUri: string; scope?: string; } export class OpenID4VCIClient { - private readonly _credentialOffer: CredentialOfferRequestWithBaseUrl; + private readonly _credentialOffer?: CredentialOfferRequestWithBaseUrl; + private _credentialIssuer: string; private _clientId?: string; private _kid: string | undefined; private _alg: Alg | string | undefined; private _endpointMetadata: EndpointMetadataResult | undefined; private _accessTokenResponse: AccessTokenResponse | undefined; - private constructor(credentialOffer: CredentialOfferRequestWithBaseUrl, kid?: string, alg?: Alg | string, clientId?: string) { + private constructor({ + credentialOffer, + clientId, + kid, + alg, + credentialIssuer + }: { + credentialOffer?: CredentialOfferRequestWithBaseUrl; + kid?: string; + alg?: Alg | string; + clientId?: string; + credentialIssuer?: string; + }) { this._credentialOffer = credentialOffer; + const issuer = + credentialIssuer ?? (credentialOffer ? getIssuerFromCredentialOfferPayload(credentialOffer.credential_offer) : undefined); + if (!issuer) { + throw Error('No credential issuer supplied or deduced from offer') + } + this._credentialIssuer = issuer this._kid = kid; this._alg = alg; this._clientId = clientId; } + public static async fromCredentialIssuer({ + kid, + alg, + retrieveServerMetadata, + clientId, + credentialIssuer, + }: { + credentialIssuer: string; + kid?: string; + alg?: Alg | string; + retrieveServerMetadata?: boolean; + clientId?: string; + }) { + const client = new OpenID4VCIClient({ kid, alg, clientId, credentialIssuer }); + if (retrieveServerMetadata === undefined || retrieveServerMetadata) { + await client.retrieveServerMetadata(); + } + return client; + } + public static async fromURI({ uri, kid, @@ -75,7 +115,12 @@ export class OpenID4VCIClient { resolveOfferUri?: boolean; clientId?: string; }): Promise { - const client = new OpenID4VCIClient(await CredentialOfferClient.fromURI(uri, { resolve: resolveOfferUri }), kid, alg, clientId); + const client = new OpenID4VCIClient({ + credentialOffer: await CredentialOfferClient.fromURI(uri, { resolve: resolveOfferUri }), + kid, + alg, + clientId, + }); if (retrieveServerMetadata === undefined || retrieveServerMetadata) { await client.retrieveServerMetadata(); @@ -86,16 +131,41 @@ export class OpenID4VCIClient { public async retrieveServerMetadata(): Promise { this.assertIssuerData(); if (!this._endpointMetadata) { - this._endpointMetadata = await MetadataClient.retrieveAllMetadataFromCredentialOffer(this.credentialOffer); + if (this.credentialOffer) { + this._endpointMetadata = await MetadataClient.retrieveAllMetadataFromCredentialOffer(this.credentialOffer); + } else if (this._credentialIssuer) { + this._endpointMetadata = await MetadataClient.retrieveAllMetadata(this._credentialIssuer); + } else { + throw Error(`Cannot retrieve issuer metadata without either a credential offer, or issuer value`); + } } return this.endpointMetadata; } + // todo: Unify this method with the par method + public createAuthorizationRequestUrl({ codeChallengeMethod, codeChallenge, authorizationDetails, redirectUri, scope }: AuthRequestOpts): string { // Scope and authorization_details can be used in the same authorization request // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-rar-23#name-relationship-to-scope-param if (!scope && !authorizationDetails) { - throw Error('Please provide a scope or authorization_details'); + if (!this.credentialOffer) { + throw Error('Please provide a scope or authorization_details'); + } + const creds = this.credentialOffer.credential_offer.credentials; + + authorizationDetails = creds + .flatMap((cred) => (typeof cred === 'string' ? this.getCredentialsSupported(true) : cred as CredentialSupported)) + .map((cred) => { + return { + ...cred, + type: 'openid_credential', + locations: [this._credentialIssuer], + format: cred.format, + } satisfies AuthDetails; + }); + if (authorizationDetails.length === 0) { + throw Error(`Could not create authorization details from credential offer. Please pass in explicit details`); + } } // todo: Probably can go with current logic in MetadataClient who will always set the authorization_endpoint when found // handling this because of the support for v1_0-08 @@ -117,7 +187,7 @@ export class OpenID4VCIClient { const queryObj: { [key: string]: string } = { response_type: ResponseType.AUTH_CODE, - code_challenge_method: codeChallengeMethod, + code_challenge_method: codeChallengeMethod ?? CodeChallengeMethod.SHA256, code_challenge: codeChallenge, authorization_details: JSON.stringify(this.handleAuthorizationDetails(authorizationDetails)), redirect_uri: redirectUri, @@ -128,7 +198,7 @@ export class OpenID4VCIClient { queryObj['client_id'] = this.clientId; } - if (this.credentialOffer.issuerState) { + if (this.credentialOffer?.issuerState) { queryObj['issuer_state'] = this.credentialOffer.issuerState; } @@ -140,6 +210,7 @@ export class OpenID4VCIClient { }); } + // todo: Unify this method with the create auth request url method public async acquirePushedAuthorizationRequestURI({ codeChallengeMethod, codeChallenge, @@ -173,7 +244,7 @@ export class OpenID4VCIClient { const queryObj: { [key: string]: string } = { response_type: ResponseType.AUTH_CODE, - code_challenge_method: codeChallengeMethod, + code_challenge_method: codeChallengeMethod ?? CodeChallengeMethod.SHA256, code_challenge: codeChallenge, authorization_details: JSON.stringify(this.handleAuthorizationDetails(authorizationDetails)), redirect_uri: redirectUri, @@ -184,7 +255,7 @@ export class OpenID4VCIClient { queryObj['client_id'] = this.clientId; } - if (this.credentialOffer.issuerState) { + if (this.credentialOffer?.issuerState) { queryObj['issuer_state'] = this.credentialOffer.issuerState; } @@ -249,6 +320,7 @@ export class OpenID4VCIClient { const response = await accessTokenClient.acquireAccessToken({ credentialOffer: this.credentialOffer, metadata: this.endpointMetadata, + credentialIssuer: this.getIssuer(), pin, codeVerifier, code, @@ -298,15 +370,22 @@ export class OpenID4VCIClient { this._kid = kid; } - const requestBuilder = CredentialRequestClientBuilder.fromCredentialOffer({ - credentialOffer: this.credentialOffer, - metadata: this.endpointMetadata, - }); + const requestBuilder = this.credentialOffer + ? CredentialRequestClientBuilder.fromCredentialOffer({ + credentialOffer: this.credentialOffer, + metadata: this.endpointMetadata, + }) + : CredentialRequestClientBuilder.fromCredentialIssuer({ + credentialIssuer: this.getIssuer(), + credentialTypes, + metadata: this.endpointMetadata, + version: this.version(), + }); requestBuilder.withTokenFromResponse(this.accessTokenResponse); if (this.endpointMetadata?.credentialIssuerMetadata) { const metadata = this.endpointMetadata.credentialIssuerMetadata; - const types = Array.isArray(credentialTypes) ? credentialTypes.sort() : [credentialTypes]; + const types = Array.isArray(credentialTypes) ? [...credentialTypes].sort() : [credentialTypes]; if (metadata.credentials_supported && Array.isArray(metadata.credentials_supported)) { let typeSupported = false; @@ -322,10 +401,11 @@ export class OpenID4VCIClient { }); if (!typeSupported) { - throw Error(`Not all credential types ${JSON.stringify(credentialTypes)} are supported by issuer ${this.getIssuer()}`); + console.log(`Not all credential types ${JSON.stringify(credentialTypes)} are present in metadata for ${this.getIssuer()}`); + // throw Error(`Not all credential types ${JSON.stringify(credentialTypes)} are supported by issuer ${this.getIssuer()}`); } } else if (metadata.credentials_supported && !Array.isArray(metadata.credentials_supported)) { - const credentialsSupported = metadata.credentials_supported as CredentialSupportedTypeV1_0_08; + const credentialsSupported = metadata.credentials_supported; if (types.some((type) => !metadata.credentials_supported || !credentialsSupported[type])) { throw Error(`Not all credential types ${JSON.stringify(credentialTypes)} are supported by issuer ${this.getIssuer()}`); } @@ -354,7 +434,7 @@ export class OpenID4VCIClient { format, }); if (response.errorBody) { - debug(`Credential request error:\r\n${response.errorBody}`); + debug(`Credential request error:\r\n${JSON.stringify(response.errorBody)}`); throw Error( `Retrieving a credential from ${this._endpointMetadata?.credential_endpoint} for issuer ${this.getIssuer()} failed with status: ${ response.origResponse.status @@ -387,7 +467,9 @@ export class OpenID4VCIClient { } getCredentialOfferTypes(): string[][] { - if (this.credentialOffer.version < OpenId4VCIVersion.VER_1_0_11) { + if (!this.credentialOffer) { + return []; + } else if (this.credentialOffer.version < OpenId4VCIVersion.VER_1_0_11) { const orig = this.credentialOffer.original_credential_offer as CredentialOfferPayloadV1_0_08; const types: string[] = typeof orig.credential_type === 'string' ? [orig.credential_type] : orig.credential_type; const result: string[][] = []; @@ -409,15 +491,15 @@ export class OpenID4VCIClient { } issuerSupportedFlowTypes(): AuthzFlowType[] { - return this.credentialOffer.supportedFlows; + return this.credentialOffer?.supportedFlows ?? [AuthzFlowType.AUTHORIZATION_CODE_FLOW]; } - get credentialOffer(): CredentialOfferRequestWithBaseUrl { + get credentialOffer(): CredentialOfferRequestWithBaseUrl | undefined { return this._credentialOffer; } public version(): OpenId4VCIVersion { - return this.credentialOffer.version; + return this.credentialOffer?.version ?? OpenId4VCIVersion.VER_1_0_11; } public get endpointMetadata(): EndpointMetadataResult { @@ -443,9 +525,6 @@ export class OpenID4VCIClient { } get clientId(): string | undefined { - /*if (!this._clientId) { - throw Error('No client id present'); - }*/ return this._clientId; } @@ -457,7 +536,7 @@ export class OpenID4VCIClient { public getIssuer(): string { this.assertIssuerData(); - return this._endpointMetadata ? this.endpointMetadata.issuer : this.getIssuer(); + return this._credentialIssuer!; } public getAccessTokenEndpoint(): string { @@ -473,8 +552,10 @@ export class OpenID4VCIClient { } private assertIssuerData(): void { - if (!this._credentialOffer) { + if (!this._credentialOffer && this.issuerSupportedFlowTypes().includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) { throw Error(`No issuance initiation or credential offer present`); + } else if (!this._credentialIssuer) { + throw Error(`No credential issuer value present`); } } From 9f06ab1e0efef89848fb6e6a2b80ed874717e580 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Thu, 21 Dec 2023 19:08:30 +0100 Subject: [PATCH 05/11] fix: Add back jwt_vc format support for older versions --- packages/callback-example/package.json | 2 +- packages/client/lib/AccessTokenClient.ts | 18 +- .../client/lib/CredentialRequestClient.ts | 5 +- .../lib/CredentialRequestClientBuilder.ts | 19 + .../__tests__/CredentialRequestClient.spec.ts | 2 +- .../lib/__tests__/data/VciDataFixtures.ts | 2 +- packages/client/package.json | 4 +- .../lib/functions/CredentialOfferUtil.ts | 2 +- .../lib/functions/CredentialRequestUtil.ts | 2 +- packages/common/lib/functions/FormatUtils.ts | 2 +- packages/common/lib/functions/HttpUtils.ts | 10 +- .../lib/functions/IssuerMetadataUtils.ts | 7 +- packages/common/lib/functions/index.ts | 1 + .../common/lib/types/Authorization.types.ts | 5 +- packages/common/lib/types/Generic.types.ts | 8 +- packages/common/lib/types/QRCode.types.ts | 4 +- packages/common/package.json | 2 +- .../lib/__tests__/ClientIssuerIT.spec.ts | 2 +- packages/issuer-rest/package.json | 4 +- packages/issuer/package.json | 2 +- pnpm-lock.yaml | 445 +++++++++++++++++- 21 files changed, 489 insertions(+), 59 deletions(-) diff --git a/packages/callback-example/package.json b/packages/callback-example/package.json index 7373224e..667fae50 100644 --- a/packages/callback-example/package.json +++ b/packages/callback-example/package.json @@ -18,7 +18,7 @@ "@sphereon/oid4vci-client": "workspace:*", "@sphereon/oid4vci-common": "workspace:*", "@sphereon/oid4vci-issuer": "workspace:*", - "@sphereon/ssi-types": "0.17.2", + "@sphereon/ssi-types": "0.17.6-unstable.23", "jose": "^4.10.0" }, "devDependencies": { diff --git a/packages/client/lib/AccessTokenClient.ts b/packages/client/lib/AccessTokenClient.ts index bab1788f..47fb7a39 100644 --- a/packages/client/lib/AccessTokenClient.ts +++ b/packages/client/lib/AccessTokenClient.ts @@ -27,9 +27,11 @@ export class AccessTokenClient { public async acquireAccessToken(opts: AccessTokenRequestOpts): Promise> { const { asOpts, pin, codeVerifier, code, redirectUri, metadata } = opts; - const credentialOffer = await assertedUniformCredentialOffer(opts.credentialOffer); - const isPinRequired = this.isPinRequiredValue(credentialOffer.credential_offer); - const issuer = getIssuerFromCredentialOfferPayload(credentialOffer.credential_offer) ?? (metadata?.issuer as string); + const credentialOffer = opts.credentialOffer ? await assertedUniformCredentialOffer(opts.credentialOffer) : undefined; + const isPinRequired = credentialOffer && this.isPinRequiredValue(credentialOffer.credential_offer); + const issuer = + opts.credentialIssuer ?? + (credentialOffer ? getIssuerFromCredentialOfferPayload(credentialOffer.credential_offer) : (metadata?.issuer as string)); if (!issuer) { throw Error('Issuer required at this point'); } @@ -83,14 +85,14 @@ export class AccessTokenClient { public async createAccessTokenRequest(opts: AccessTokenRequestOpts): Promise { const { asOpts, pin, codeVerifier, code, redirectUri } = opts; - const credentialOfferRequest = await toUniformCredentialOfferRequest(opts.credentialOffer); + const credentialOfferRequest = opts.credentialOffer ? await toUniformCredentialOfferRequest(opts.credentialOffer) : undefined; const request: Partial = {}; if (asOpts?.clientId) { request.client_id = asOpts.clientId; } - if (credentialOfferRequest.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) { + if (credentialOfferRequest?.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) { this.assertNumericPin(this.isPinRequiredValue(credentialOfferRequest.credential_offer), pin); request.user_pin = pin; @@ -102,7 +104,7 @@ export class AccessTokenClient { return request as AccessTokenRequest; } - if (credentialOfferRequest.supportedFlows.includes(AuthzFlowType.AUTHORIZATION_CODE_FLOW)) { + if (!credentialOfferRequest || credentialOfferRequest.supportedFlows.includes(AuthzFlowType.AUTHORIZATION_CODE_FLOW)) { request.grant_type = GrantTypes.AUTHORIZATION_CODE; request.code = code; request.redirect_uri = redirectUri; @@ -243,7 +245,7 @@ export class AccessTokenClient { } private throwNotSupportedFlow(): void { - debug(`Only pre-authorized flow supported.`); - throw new Error('Only pre-authorized-code flow is supported'); + debug(`Only pre-authorized or authorization code flows supported.`); + throw new Error('Only pre-authorized-code or authorization code flows are supported'); } } diff --git a/packages/client/lib/CredentialRequestClient.ts b/packages/client/lib/CredentialRequestClient.ts index 8bcdfb89..fe2966c7 100644 --- a/packages/client/lib/CredentialRequestClient.ts +++ b/packages/client/lib/CredentialRequestClient.ts @@ -61,9 +61,10 @@ export class CredentialRequestClient { throw new Error(URL_NOT_VALID); } debug(`Acquiring credential(s) from: ${credentialEndpoint}`); + debug(`request\n: ${JSON.stringify(request, null, 2)}`); const requestToken: string = this.credentialRequestOpts.token; const response: OpenIDResponse = await post(credentialEndpoint, JSON.stringify(request), { bearerToken: requestToken }); - debug(`Credential endpoint ${credentialEndpoint} response:\r\n${response}`); + debug(`Credential endpoint ${credentialEndpoint} response:\r\n${JSON.stringify(response, null, 2)}`); return response; } @@ -99,7 +100,7 @@ export class CredentialRequestClient { : await proofInput.build(); // TODO: we should move format specific logic - if (format === 'jwt_vc_json') { + if (format === 'jwt_vc_json' || format === 'jwt_vc') { return { types, format, diff --git a/packages/client/lib/CredentialRequestClientBuilder.ts b/packages/client/lib/CredentialRequestClientBuilder.ts index c3a0498e..8d953825 100644 --- a/packages/client/lib/CredentialRequestClientBuilder.ts +++ b/packages/client/lib/CredentialRequestClientBuilder.ts @@ -23,6 +23,25 @@ export class CredentialRequestClientBuilder { token?: string; version?: OpenId4VCIVersion; + public static fromCredentialIssuer({ + credentialIssuer, + metadata, + version, + credentialTypes, + }: { + credentialIssuer: string; + metadata?: EndpointMetadata; + version?: OpenId4VCIVersion; + credentialTypes: string | string[]; + }): CredentialRequestClientBuilder { + const issuer = credentialIssuer; + const builder = new CredentialRequestClientBuilder(); + builder.withVersion(version ?? OpenId4VCIVersion.VER_1_0_11); + builder.withCredentialEndpoint(metadata?.credential_endpoint ?? (issuer.endsWith('/') ? `${issuer}credential` : `${issuer}/credential`)); + builder.withCredentialType(credentialTypes); + return builder; + } + public static async fromURI({ uri, metadata }: { uri: string; metadata?: EndpointMetadata }): Promise { const offer = await CredentialOfferClient.fromURI(uri); return CredentialRequestClientBuilder.fromCredentialOfferRequest({ request: offer, ...offer, metadata, version: offer.version }); diff --git a/packages/client/lib/__tests__/CredentialRequestClient.spec.ts b/packages/client/lib/__tests__/CredentialRequestClient.spec.ts index c908a5a9..dc87a1d4 100644 --- a/packages/client/lib/__tests__/CredentialRequestClient.spec.ts +++ b/packages/client/lib/__tests__/CredentialRequestClient.spec.ts @@ -128,7 +128,7 @@ describe('Credential Request Client ', () => { version: OpenId4VCIVersion.VER_1_0_08, }); expect(credentialRequest.proof?.jwt?.includes(partialJWT)).toBeTruthy(); - expect(credentialRequest.format).toEqual('jwt_vc_json'); + expect(credentialRequest.format).toEqual('jwt_vc'); const result = await credReqClient.acquireCredentialsUsingRequest(credentialRequest); expect(result?.successBody?.credential).toEqual(mockedVC); }); diff --git a/packages/client/lib/__tests__/data/VciDataFixtures.ts b/packages/client/lib/__tests__/data/VciDataFixtures.ts index afce2671..81653cd5 100644 --- a/packages/client/lib/__tests__/data/VciDataFixtures.ts +++ b/packages/client/lib/__tests__/data/VciDataFixtures.ts @@ -358,7 +358,7 @@ const mockData: VciMockDataStructure = { url: 'https://jff.walt.id/issuer-api/default/oidc/credential', request: { types: ['OpenBadgeCredential'], - format: 'jwt_vc_json', + format: 'jwt_vc', proof: { proof_type: 'jwt', jwt: 'eyJ0eXAiOiJKV1QiLCJhbGciOiJFUzI1NksiLCJraWQiOiJkaWQ6andrOmV5SmhiR2NpT2lKRlV6STFOa3NpTENKMWMyVWlPaUp6YVdjaUxDSnJkSGtpT2lKRlF5SXNJbU55ZGlJNkluTmxZM0F5TlRack1TSXNJbmdpT2lKclpuVmpTa0V0VEhKck9VWjBPRmx5TFVkMlQzSmpia3N3YjNkc2RqUlhNblUwU3pJeFNHZHZTVlIzSWl3aWVTSTZJalozY0ZCUE1rOUNRVXBTU0ZFMVRXdEtXVlJaV0dsQlJFUXdOMU5OTlV0amVXcDNYMkUzVUUxWmVGa2lmUSMwIn0.eyJhdWQiOiJodHRwczovL2pmZi53YWx0LmlkL2lzc3Vlci1hcGkvZGVmYXVsdC9vaWRjLyIsImlhdCI6MTY4MTkxMTk0Mi4yMzgsImV4cCI6MTY4MTkxMjYwMi4yMzgsIm5vbmNlIjoiZjA2YTMxMDUtYTJlZC00NGZjLTk1NGItNGEyNTk3MDM0OTNiIiwiaXNzIjoic3BoZXJlb246c3NpLXdhbGxldCIsImp0aSI6IjA1OWM3ODA5LTlmOGYtNGE3ZS1hZDI4YTNhMTNhMGIzNmViIn0.RfiWyybxpe3nkx3b0yIsqDHQtvB1WwhDW4t0X-kijy2dsSfv2cYhSEmAzs1shg7OV4EW8fSzt_Te79xiVl6jCw', diff --git a/packages/client/package.json b/packages/client/package.json index 1d1c5a01..3969ee1e 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -16,7 +16,7 @@ }, "dependencies": { "@sphereon/oid4vci-common": "workspace:*", - "@sphereon/ssi-types": "0.17.2", + "@sphereon/ssi-types": "0.17.6-unstable.23", "cross-fetch": "^3.1.8", "debug": "^4.3.4" }, @@ -25,6 +25,7 @@ "@types/node": "^18.17.4", "@typescript-eslint/eslint-plugin": "^5.62.0", "@typescript-eslint/parser": "^5.62.0", + "@sphereon/ssi-sdk-ext.key-utils": "^0.15.1-next.7", "codecov": "^3.8.3", "dotenv": "^16.3.1", "eslint": "^8.46.0", @@ -38,6 +39,7 @@ "npm-run-all": "^4.1.5", "uuid": "^9.0.1", "@transmute/did-key.js": "^0.3.0-unstable.10", + "@trust/keyto": "^2.0.0-alpha1", "@types/uuid": "^9.0.6", "open-cli": "^7.2.0", "ts-jest": "^29.1.1", diff --git a/packages/common/lib/functions/CredentialOfferUtil.ts b/packages/common/lib/functions/CredentialOfferUtil.ts index 7f2428f1..74e2bf4f 100644 --- a/packages/common/lib/functions/CredentialOfferUtil.ts +++ b/packages/common/lib/functions/CredentialOfferUtil.ts @@ -350,7 +350,7 @@ export function getTypesFromOffer(credentialOffer: UniformCredentialOfferPayload return [...prev, curr]; } else if (curr.format === 'jwt_vc_json-ld' || curr.format === 'ldp_vc') { return [...prev, ...curr.credential_definition.types]; - } else if (curr.format === 'jwt_vc_json') { + } else if (curr.format === 'jwt_vc_json' || curr.format === 'jwt_vc') { return [...prev, ...curr.types]; } else if (curr.format === 'vc+sd-jwt') { return [...prev, curr.credential_definition.vct]; diff --git a/packages/common/lib/functions/CredentialRequestUtil.ts b/packages/common/lib/functions/CredentialRequestUtil.ts index 66ee9fe6..4efdd5b4 100644 --- a/packages/common/lib/functions/CredentialRequestUtil.ts +++ b/packages/common/lib/functions/CredentialRequestUtil.ts @@ -4,7 +4,7 @@ import { getFormatForVersion } from './FormatUtils'; export function getTypesFromRequest(credentialRequest: UniformCredentialRequest, opts?: { filterVerifiableCredential: boolean }) { let types: string[] = []; - if (credentialRequest.format === 'jwt_vc_json') { + if (credentialRequest.format === 'jwt_vc_json' || credentialRequest.format === 'jwt_vc') { types = credentialRequest.types; } else if (credentialRequest.format === 'jwt_vc_json-ld' || credentialRequest.format === 'ldp_vc') { types = credentialRequest.credential_definition.types; diff --git a/packages/common/lib/functions/FormatUtils.ts b/packages/common/lib/functions/FormatUtils.ts index 2eda8449..ed2a6b08 100644 --- a/packages/common/lib/functions/FormatUtils.ts +++ b/packages/common/lib/functions/FormatUtils.ts @@ -28,7 +28,7 @@ export function getUniformFormat(format: string | OID4VCICredentialFormat | Cred // Older formats if (format === 'jwt_vc' || format === 'jwt') { - return 'jwt_vc_json'; + return 'jwt_vc'; } if (format === 'ldp_vc' || format === 'ldp') { return 'ldp_vc'; diff --git a/packages/common/lib/functions/HttpUtils.ts b/packages/common/lib/functions/HttpUtils.ts index 6090bef8..757f6f97 100644 --- a/packages/common/lib/functions/HttpUtils.ts +++ b/packages/common/lib/functions/HttpUtils.ts @@ -8,7 +8,7 @@ const debug = Debug('sphereon:openid4vci:http'); export const getJson = async ( URL: string, opts?: { - bearerToken?: string; + bearerToken?: (() => Promise) | string; contentType?: string; accept?: string; customHeaders?: Record; @@ -22,7 +22,7 @@ export const formPost = async ( url: string, body: BodyInit, opts?: { - bearerToken?: string; + bearerToken?: (() => Promise) | string; contentType?: string; accept?: string; customHeaders?: Record; @@ -36,7 +36,7 @@ export const post = async ( url: string, body?: BodyInit, opts?: { - bearerToken?: string; + bearerToken?: (() => Promise) | string; contentType?: string; accept?: string; customHeaders?: Record; @@ -51,7 +51,7 @@ const openIdFetch = async ( body?: BodyInit, opts?: { method?: string; - bearerToken?: string; + bearerToken?: (() => Promise) | string; contentType?: string; accept?: string; customHeaders?: Record; @@ -60,7 +60,7 @@ const openIdFetch = async ( ): Promise> => { const headers: Record = opts?.customHeaders ?? {}; if (opts?.bearerToken) { - headers['Authorization'] = `Bearer ${opts.bearerToken}`; + headers['Authorization'] = `Bearer ${typeof opts.bearerToken === 'function' ? await opts.bearerToken() : opts.bearerToken}`; } const method = opts?.method ? opts.method : body ? 'POST' : 'GET'; const accept = opts?.accept ? opts.accept : 'application/json'; diff --git a/packages/common/lib/functions/IssuerMetadataUtils.ts b/packages/common/lib/functions/IssuerMetadataUtils.ts index 40eb85d6..5bf406cc 100644 --- a/packages/common/lib/functions/IssuerMetadataUtils.ts +++ b/packages/common/lib/functions/IssuerMetadataUtils.ts @@ -101,7 +101,12 @@ export function getSupportedCredential(opts?: { export function getTypesFromCredentialSupported(credentialSupported: CredentialSupported, opts?: { filterVerifiableCredential: boolean }) { let types: string[] = []; - if (credentialSupported.format === 'jwt_vc_json' || credentialSupported.format === 'jwt_vc_json-ld' || credentialSupported.format === 'ldp_vc') { + if ( + credentialSupported.format === 'jwt_vc_json' || + credentialSupported.format === 'jwt_vc' || + credentialSupported.format === 'jwt_vc_json-ld' || + credentialSupported.format === 'ldp_vc' + ) { types = credentialSupported.types; } else if (credentialSupported.format === 'vc+sd-jwt') { types = [credentialSupported.credential_definition.vct]; diff --git a/packages/common/lib/functions/index.ts b/packages/common/lib/functions/index.ts index f192cab0..83f69014 100644 --- a/packages/common/lib/functions/index.ts +++ b/packages/common/lib/functions/index.ts @@ -2,4 +2,5 @@ export * from './CredentialRequestUtil'; export * from './CredentialOfferUtil'; export * from './Encoding'; export * from './TypeConversionUtils'; +export * from './IssuerMetadataUtils'; export * from './FormatUtils'; diff --git a/packages/common/lib/types/Authorization.types.ts b/packages/common/lib/types/Authorization.types.ts index 0276d510..b8240835 100644 --- a/packages/common/lib/types/Authorization.types.ts +++ b/packages/common/lib/types/Authorization.types.ts @@ -111,7 +111,7 @@ export interface CommonAuthorizationDetails { } export interface AuthorizationDetailsJwtVcJson extends CommonAuthorizationDetails { - format: 'jwt_vc_json'; + format: 'jwt_vc_json' | 'jwt_vc'; // jwt_vc added for backward compat /** * A JSON object containing a list of key value pairs, where the key identifies the claim offered in the Credential. @@ -177,7 +177,8 @@ export interface IssuerOpts { } export interface AccessTokenRequestOpts { - credentialOffer: UniformCredentialOffer; + credentialOffer?: UniformCredentialOffer; + credentialIssuer?: string; asOpts?: AuthorizationServerOpts; metadata?: EndpointMetadata; codeVerifier?: string; // only required for authorization flow diff --git a/packages/common/lib/types/Generic.types.ts b/packages/common/lib/types/Generic.types.ts index 16e042df..7df6c4a7 100644 --- a/packages/common/lib/types/Generic.types.ts +++ b/packages/common/lib/types/Generic.types.ts @@ -15,7 +15,7 @@ export interface ImageInfo { [key: string]: unknown; } -export type OID4VCICredentialFormat = 'jwt_vc_json' | 'jwt_vc_json-ld' | 'ldp_vc' | 'vc+sd-jwt' /*| 'mso_mdoc'*/; // we do not support mdocs at this point +export type OID4VCICredentialFormat = 'jwt_vc_json' | 'jwt_vc_json-ld' | 'ldp_vc' | 'vc+sd-jwt' | 'jwt_vc'; // jwt_vc is added for backwards compat /*| 'mso_mdoc'*/; // we do not support mdocs at this point export interface NameAndLocale { name?: string; // REQUIRED. String value of a display name for the Credential. @@ -87,7 +87,7 @@ export interface CredentialSupportedJwtVcJson extends CommonCredentialSupported types: string[]; // REQUIRED. JSON array designating the types a certain credential type supports credentialSubject?: IssuerCredentialSubject; // OPTIONAL. A JSON object containing a list of key value pairs, where the key identifies the claim offered in the Credential. The value MAY be a dictionary, which allows to represent the full (potentially deeply nested) structure of the verifiable credential to be issued. order?: string[]; //An array of claims.display.name values that lists them in the order they should be displayed by the Wallet. - format: 'jwt_vc_json'; + format: 'jwt_vc_json' | 'jwt_vc'; // jwt_vc added for backwards compat } export interface SdJwtVcCredentialDefinition { @@ -117,7 +117,7 @@ export interface CredentialOfferFormatJwtVcJsonLdAndLdpVc extends CommonCredenti } export interface CredentialOfferFormatJwtVcJson extends CommonCredentialOfferFormat { - format: 'jwt_vc_json'; + format: 'jwt_vc_json' | 'jwt_vc'; // jwt_vc is added for backwards compat types: string[]; // REQUIRED. JSON array as defined in Appendix E.1.1.2. This claim contains the type values the Wallet shall request in the subsequent Credential Request. } @@ -164,7 +164,7 @@ export interface CommonCredentialRequest { } export interface CredentialRequestJwtVcJson extends CommonCredentialRequest { - format: 'jwt_vc_json'; + format: 'jwt_vc_json' | 'jwt_vc'; // jwt_vc for backwards compat types: string[]; credentialSubject?: IssuerCredentialSubject; } diff --git a/packages/common/lib/types/QRCode.types.ts b/packages/common/lib/types/QRCode.types.ts index 99deb63b..16280dcb 100644 --- a/packages/common/lib/types/QRCode.types.ts +++ b/packages/common/lib/types/QRCode.types.ts @@ -60,7 +60,7 @@ export interface ComponentOptions { */ protectors?: boolean; }; -}; +} export interface QRCodeOpts { /** @@ -224,4 +224,4 @@ export interface QRCodeOpts { * @deafultValue 0.4 */ dotScale?: number; -}; +} diff --git a/packages/common/package.json b/packages/common/package.json index ff5ff6bd..eeb5f78f 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -10,7 +10,7 @@ "build:clean": "tsc --build --clean && tsc --build" }, "dependencies": { - "@sphereon/ssi-types": "0.17.2", + "@sphereon/ssi-types": "0.17.6-unstable.23", "cross-fetch": "^3.1.8", "jwt-decode": "^3.1.2" }, diff --git a/packages/issuer-rest/lib/__tests__/ClientIssuerIT.spec.ts b/packages/issuer-rest/lib/__tests__/ClientIssuerIT.spec.ts index c19316be..c0ef72d5 100644 --- a/packages/issuer-rest/lib/__tests__/ClientIssuerIT.spec.ts +++ b/packages/issuer-rest/lib/__tests__/ClientIssuerIT.spec.ts @@ -296,7 +296,7 @@ describe('VcIssuer', () => { }) it('should get state on server side', async () => { const preAuthCode = - client.credentialOffer.credential_offer.grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.['pre-authorized_code'] + client.credentialOffer!.credential_offer.grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.['pre-authorized_code'] expect(preAuthCode).toBeDefined() if (preAuthCode) { diff --git a/packages/issuer-rest/package.json b/packages/issuer-rest/package.json index 3f1e5ede..62418c0f 100644 --- a/packages/issuer-rest/package.json +++ b/packages/issuer-rest/package.json @@ -13,8 +13,8 @@ "dependencies": { "@sphereon/oid4vci-common": "workspace:*", "@sphereon/oid4vci-issuer": "workspace:*", - "@sphereon/ssi-express-support": "0.17.2", - "@sphereon/ssi-types": "0.17.2", + "@sphereon/ssi-express-support": "0.17.6-unstable.23", + "@sphereon/ssi-types": "0.17.6-unstable.23", "body-parser": "^1.20.2", "cookie-parser": "^1.4.6", "cors": "^2.8.5", diff --git a/packages/issuer/package.json b/packages/issuer/package.json index 5a68335c..89f2cc75 100644 --- a/packages/issuer/package.json +++ b/packages/issuer/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@sphereon/oid4vci-common": "workspace:*", - "@sphereon/ssi-types": "0.17.2", + "@sphereon/ssi-types": "0.17.6-unstable.23", "uuid": "^9.0.0" }, "peerDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cbe61181..2b99ee3d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,8 +75,8 @@ importers: specifier: workspace:* version: link:../issuer '@sphereon/ssi-types': - specifier: 0.17.2 - version: 0.17.2 + specifier: 0.17.6-unstable.23 + version: 0.17.6-unstable.23 jose: specifier: ^4.10.0 version: 4.14.6 @@ -118,8 +118,8 @@ importers: specifier: workspace:* version: link:../common '@sphereon/ssi-types': - specifier: 0.17.2 - version: 0.17.2 + specifier: 0.17.6-unstable.23 + version: 0.17.6-unstable.23 cross-fetch: specifier: ^3.1.8 version: 3.1.8 @@ -127,9 +127,15 @@ importers: specifier: ^4.3.4 version: 4.3.4 devDependencies: + '@sphereon/ssi-sdk-ext.key-utils': + specifier: ^0.15.1-next.7 + version: 0.15.1-next.7(expo-crypto@12.6.0)(expo@48.0.20)(msrcrypto@1.5.8)(react-native-securerandom@1.0.1) '@transmute/did-key.js': specifier: ^0.3.0-unstable.10 version: 0.3.0-unstable.10 + '@trust/keyto': + specifier: ^2.0.0-alpha1 + version: 2.0.0-alpha1 '@types/jest': specifier: ^29.5.3 version: 29.5.5 @@ -200,8 +206,8 @@ importers: packages/common: dependencies: '@sphereon/ssi-types': - specifier: 0.17.2 - version: 0.17.2 + specifier: 0.17.6-unstable.23 + version: 0.17.6-unstable.23 cross-fetch: specifier: ^3.1.8 version: 3.1.8 @@ -222,8 +228,8 @@ importers: specifier: workspace:* version: link:../common '@sphereon/ssi-types': - specifier: 0.17.2 - version: 0.17.2 + specifier: 0.17.6-unstable.23 + version: 0.17.6-unstable.23 awesome-qr: specifier: ^2.1.5-rc.0 version: 2.1.5-rc.0 @@ -256,11 +262,11 @@ importers: specifier: workspace:* version: link:../issuer '@sphereon/ssi-express-support': - specifier: 0.17.2 - version: 0.17.2 + specifier: 0.17.6-unstable.23 + version: 0.17.6-unstable.23 '@sphereon/ssi-types': - specifier: 0.17.2 - version: 0.17.2 + specifier: 0.17.6-unstable.23 + version: 0.17.6-unstable.23 body-parser: specifier: ^1.20.2 version: 1.20.2 @@ -1969,6 +1975,23 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@ethersproject/bytes@5.7.0: + resolution: {integrity: sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==} + dependencies: + '@ethersproject/logger': 5.7.0 + dev: true + + /@ethersproject/logger@5.7.0: + resolution: {integrity: sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==} + dev: true + + /@ethersproject/random@5.7.0: + resolution: {integrity: sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + dev: true + /@expo/bunyan@4.0.0: resolution: {integrity: sha512-Ydf4LidRB/EBI+YrB+cVLqIseiRfjUI/AeHBgjGMtq3GroraDu81OV7zqophRgupngoL3iS3JUMDMnxO7g39qA==} engines: {'0': node >=0.10.0} @@ -2770,10 +2793,29 @@ packages: dev: true optional: true + /@multiformats/base-x@4.0.1: + resolution: {integrity: sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw==} + dev: true + + /@noble/ciphers@0.4.0: + resolution: {integrity: sha512-xaUaUUDWbHIFSxaQ/pIe+33VG2mfJp6N/KxKLmZr5biWdNznCAmfu24QRhX10BbVAuqOahAoyp0S4M9md6GPDw==} + dev: true + + /@noble/curves@1.2.0: + resolution: {integrity: sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==} + dependencies: + '@noble/hashes': 1.3.2 + dev: true + /@noble/ed25519@1.7.3: resolution: {integrity: sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==} dev: false + /@noble/hashes@1.3.2: + resolution: {integrity: sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==} + engines: {node: '>= 16'} + dev: true + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -3349,6 +3391,10 @@ packages: /@react-native/polyfills@2.0.0: resolution: {integrity: sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ==} + /@scure/base@1.1.3: + resolution: {integrity: sha512-/+SgoRjLq7Xlf0CWuLHq2LUZeL/w65kfzAPG5NH9pcmBhs+nunQTn4gvdwgMTIXnt9b2C/1SeL2XiysZEyIC9Q==} + dev: true + /@segment/loosely-validate-event@2.0.0: resolution: {integrity: sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw==} dependencies: @@ -3412,8 +3458,35 @@ packages: dependencies: '@sinonjs/commons': 3.0.0 - /@sphereon/ssi-express-support@0.17.2: - resolution: {integrity: sha512-OrLC7YAelpUmCIzPRgHM97HBNFqDoSdJNNssstS6Ho0ZXswq4fsPDm+h49+//ogp1ERbuOl9Ywqhp+3DdLZCPA==} + /@sphereon/isomorphic-webcrypto@2.4.0-unstable.4(expo-crypto@12.6.0)(expo@48.0.20)(msrcrypto@1.5.8)(react-native-securerandom@1.0.1): + resolution: {integrity: sha512-7i9GBta0yji3Z5ocyk82fXpqrV/swe7hXZVfVzOXRaGtTUNd+y8W/3cpHRQC2S4UEO/5N3lX7+B6qUunK9wS/Q==} + peerDependencies: + expo: '*' + expo-crypto: '*' + msrcrypto: ^1.5.8 + react-native-securerandom: ^1.0.1 + dependencies: + '@peculiar/webcrypto': 1.4.3 + asmcrypto.js: 2.3.2 + b64-lite: 1.4.0 + b64u-lite: 1.1.0 + cipher-base: 1.0.4 + create-hash: 1.2.0 + expo: 48.0.20(@babel/core@7.23.0) + expo-crypto: 12.6.0(expo@48.0.20) + inherits: 2.0.4 + md5.js: 1.3.5 + msrcrypto: 1.5.8 + randomfill: 1.0.4 + react-native-securerandom: 1.0.1(react-native@0.71.13) + ripemd160: 2.0.2 + sha.js: 2.4.11 + str2buf: 1.3.0 + webcrypto-shim: 0.1.7 + dev: true + + /@sphereon/ssi-express-support@0.17.6-unstable.23: + resolution: {integrity: sha512-1F6vhl0E0119H/UYM2mc9DUNOECb2VlVucfhQUdI+Znv6SCisgUw946GzalyqEt6EIkbAO7cN0KapD7m0g2Tww==} peerDependencies: '@noble/hashes': 1.2.0 passport-azure-ad: ^4.3.5 @@ -3443,12 +3516,42 @@ packages: - supports-color dev: false - /@sphereon/ssi-types@0.17.2: - resolution: {integrity: sha512-Qo1dkISavtPIe1WKZXZGyHvquoUvdUlDI0GLzb21clKFPuxbawXdlxpCqOh6NCNRfX7ohEeCUQdEA1PNBlnKYA==} + /@sphereon/ssi-sdk-ext.key-utils@0.15.1-next.7(expo-crypto@12.6.0)(expo@48.0.20)(msrcrypto@1.5.8)(react-native-securerandom@1.0.1): + resolution: {integrity: sha512-dEvAricZE6+lFEQocW+gFw1dV1ZQNIu8BnQOeLk7RIkb0BoR6VE0W+dv3jw6HV24LSqVLjD+R5q0g2ZrYB+ekw==} + dependencies: + '@ethersproject/random': 5.7.0 + '@sphereon/isomorphic-webcrypto': 2.4.0-unstable.4(expo-crypto@12.6.0)(expo@48.0.20)(msrcrypto@1.5.8)(react-native-securerandom@1.0.1) + '@stablelib/ed25519': 1.0.3 + '@stablelib/sha256': 1.0.1 + '@stablelib/sha512': 1.0.1 + '@veramo/core': 4.2.0 + base64url: 3.0.1 + debug: 4.3.4 + did-resolver: 4.1.0 + elliptic: 6.5.4 + lodash.isplainobject: 4.0.6 + multiformats: 9.9.0 + uint8arrays: 3.1.1 + varint: 6.0.0 + web-encoding: 1.1.5 + transitivePeerDependencies: + - expo + - expo-crypto + - msrcrypto + - react-native-securerandom + - supports-color + dev: true + + /@sphereon/ssi-types@0.17.6-unstable.23: + resolution: {integrity: sha512-ksMaQq8i0tlUc5uo+iinRFwEYXE5VM3QuOpYzeXtwqNRbbklCmf0mZR24XQGfu+vYH6RVf33U9j0hAaEqKU3ew==} dependencies: jwt-decode: 3.1.2 dev: false + /@stablelib/aead@1.0.1: + resolution: {integrity: sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg==} + dev: true + /@stablelib/binary@1.0.1: resolution: {integrity: sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==} dependencies: @@ -3458,6 +3561,28 @@ packages: resolution: {integrity: sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==} dev: true + /@stablelib/chacha20poly1305@1.0.1: + resolution: {integrity: sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA==} + dependencies: + '@stablelib/aead': 1.0.1 + '@stablelib/binary': 1.0.1 + '@stablelib/chacha': 1.0.1 + '@stablelib/constant-time': 1.0.1 + '@stablelib/poly1305': 1.0.1 + '@stablelib/wipe': 1.0.1 + dev: true + + /@stablelib/chacha@1.0.1: + resolution: {integrity: sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg==} + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/wipe': 1.0.1 + dev: true + + /@stablelib/constant-time@1.0.1: + resolution: {integrity: sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg==} + dev: true + /@stablelib/ed25519@1.0.3: resolution: {integrity: sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==} dependencies: @@ -3477,6 +3602,13 @@ packages: '@stablelib/bytes': 1.0.1 dev: true + /@stablelib/poly1305@1.0.1: + resolution: {integrity: sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA==} + dependencies: + '@stablelib/constant-time': 1.0.1 + '@stablelib/wipe': 1.0.1 + dev: true + /@stablelib/random@1.0.0: resolution: {integrity: sha512-G9vwwKrNCGMI/uHL6XeWe2Nk4BuxkYyWZagGaDU9wrsuV+9hUwNI1lok2WVo8uJDa2zx7ahNwN7Ij983hOUFEw==} dependencies: @@ -3490,6 +3622,14 @@ packages: '@stablelib/binary': 1.0.1 '@stablelib/wipe': 1.0.1 + /@stablelib/sha256@1.0.1: + resolution: {integrity: sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ==} + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/hash': 1.0.1 + '@stablelib/wipe': 1.0.1 + dev: true + /@stablelib/sha512@1.0.1: resolution: {integrity: sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==} dependencies: @@ -3508,6 +3648,24 @@ packages: '@stablelib/wipe': 1.0.1 dev: true + /@stablelib/xchacha20@1.0.1: + resolution: {integrity: sha512-1YkiZnFF4veUwBVhDnDYwo6EHeKzQK4FnLiO7ezCl/zu64uG0bCCAUROJaBkaLH+5BEsO3W7BTXTguMbSLlWSw==} + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/chacha': 1.0.1 + '@stablelib/wipe': 1.0.1 + dev: true + + /@stablelib/xchacha20poly1305@1.0.1: + resolution: {integrity: sha512-B1Abj0sMJ8h3HNmGnJ7vHBrAvxuNka6cJJoZ1ILN7iuacXp7sUYcgOVEOTLWj+rtQMpspY9tXSCRLPmN1mQNWg==} + dependencies: + '@stablelib/aead': 1.0.1 + '@stablelib/chacha20poly1305': 1.0.1 + '@stablelib/constant-time': 1.0.1 + '@stablelib/wipe': 1.0.1 + '@stablelib/xchacha20': 1.0.1 + dev: true + /@tokenizer/token@0.3.0: resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} dev: true @@ -3685,6 +3843,14 @@ packages: '@transmute/ld-key-pair': 0.7.0-unstable.81 dev: true + /@trust/keyto@2.0.0-alpha1: + resolution: {integrity: sha512-VmlOa+nOaDzhEUfprnVp7RxFQyuEwA4fJ5+smnsud5WM01gU16yQnO/ejZnDVMGXuq/sUwTa5pCej4JhkKA5Sg==} + dependencies: + asn1.js: 5.4.1 + base64url: 3.0.1 + elliptic: 6.5.4 + dev: true + /@tsconfig/node10@1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true @@ -4090,6 +4256,19 @@ packages: graphql: 15.8.0 wonka: 4.0.15 + /@veramo/core@4.2.0: + resolution: {integrity: sha512-HIqbXfCbwOAJelR5Ohsm22vr63cy6ND8Ua/+9wfMDAiymUUS7NryaJ/v6NRtnmIrNZqUMDdR9/TWdp4cCq5eBg==} + dependencies: + credential-status: 2.0.6 + debug: 4.3.4 + did-jwt-vc: 3.2.14 + did-resolver: 4.1.0 + events: 3.3.0 + z-schema: 5.0.5 + transitivePeerDependencies: + - supports-color + dev: true + /@xmldom/xmldom@0.7.13: resolution: {integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==} engines: {node: '>=10.0.0'} @@ -4117,6 +4296,12 @@ packages: argparse: 2.0.1 dev: true + /@zxing/text-encoding@0.9.0: + resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} + requiresBuild: true + dev: true + optional: true + /JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -4438,6 +4623,19 @@ packages: resolution: {integrity: sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA==} dev: false + /asmcrypto.js@2.3.2: + resolution: {integrity: sha512-3FgFARf7RupsZETQ1nHnhLUUvpcttcCq1iZCaVAbJZbCZ5VNRrNyvpDyHTOb0KC3llFcsyOT/a99NZcCbeiEsA==} + dev: true + + /asn1.js@5.4.1: + resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} + dependencies: + bn.js: 4.12.0 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + safer-buffer: 2.1.2 + dev: true + /asn1js@3.0.5: resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==} engines: {node: '>=12.0.0'} @@ -4503,13 +4701,11 @@ packages: resolution: {integrity: sha512-aHe97M7DXt+dkpa8fHlCcm1CnskAHrJqEfMI0KN7dwqlzml/aUe1AGt6lk51HzrSfVD67xOso84sOpr+0wIe2w==} dependencies: base-64: 0.1.0 - dev: false /b64u-lite@1.1.0: resolution: {integrity: sha512-929qWGDVCRph7gQVTC6koHqQIpF4vtVaSbwLltFQo44B1bYUquALswZdBKFfrJCPEnsCOvWkJsPdQYZ/Ukhw8A==} dependencies: b64-lite: 1.4.0 - dev: false /babel-core@7.0.0-bridge.0(@babel/core@7.23.0): resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} @@ -4692,7 +4888,6 @@ packages: /base-64@0.1.0: resolution: {integrity: sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==} - dev: false /base-x@3.0.9: resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==} @@ -4724,6 +4919,10 @@ packages: safe-buffer: 5.1.2 dev: false + /bech32@2.0.0: + resolution: {integrity: sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==} + dev: true + /before-after-hook@2.2.3: resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} dev: true @@ -5033,6 +5232,10 @@ packages: /canonicalize@1.0.8: resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==} + /canonicalize@2.0.0: + resolution: {integrity: sha512-ulDEYPv7asdKvqahuAY35c1selLdzDwHqugK92hfkzvlDCwXRRelDkR+Er33md/PtnpqHemgkuDPanZ4fiYZ8w==} + dev: true + /canvas@2.11.2: resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==} engines: {node: '>=6'} @@ -5102,6 +5305,13 @@ packages: resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} engines: {node: '>=8'} + /cipher-base@1.0.4: + resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + /cjs-module-lexer@1.2.3: resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} dev: true @@ -5563,6 +5773,16 @@ packages: typescript: 5.0.4 dev: true + /create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + dependencies: + cipher-base: 1.0.4 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.2 + sha.js: 2.4.11 + dev: true + /create-jest@29.7.0(@types/node@18.18.0)(ts-node@10.9.1): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5586,6 +5806,13 @@ packages: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true + /credential-status@2.0.6: + resolution: {integrity: sha512-l5ZwSbX/UXFJ3DQ3dFt4rc2BtfUu/rhlkefR7BL9EZsKPyCe21okJA9mDy4h/nXvMEwpYjSQEa5vzR7KZqhI9g==} + dependencies: + did-jwt: 6.11.6 + did-resolver: 4.1.0 + dev: true + /credentials-context@2.0.0: resolution: {integrity: sha512-/mFKax6FK26KjgV2KW2D4YqKgoJ5DVJpNt87X2Jc9IxT2HBMy7nEIlc+n7pEi+YFFe721XqrvZPd+jbyyBjsvQ==} dev: false @@ -5874,6 +6101,45 @@ packages: resolution: {integrity: sha512-iFpszgSxc7d1kNBJWC+PAzNTpe5LPalzsIunTMIpbG3O37Q7Zi7u4iIaedaM7UhziBhT+Agr9DyvAiXSUyfepQ==} dev: false + /did-jwt-vc@3.2.14: + resolution: {integrity: sha512-5O07lNiWQ3eUwYzJBuF6YFx/yqTlbqkcYNhP/Q4dmGN6Pwf8pfrU9fTb0alIfSwmvlyLk2fb2FkVi03iNk4jGQ==} + engines: {node: '>=18'} + dependencies: + did-jwt: 7.4.5 + did-resolver: 4.1.0 + dev: true + + /did-jwt@6.11.6: + resolution: {integrity: sha512-OfbWknRxJuUqH6Lk0x+H1FsuelGugLbBDEwsoJnicFOntIG/A4y19fn0a8RLxaQbWQ5gXg0yDq5E2huSBiiXzw==} + dependencies: + '@stablelib/ed25519': 1.0.3 + '@stablelib/random': 1.0.2 + '@stablelib/sha256': 1.0.1 + '@stablelib/x25519': 1.0.3 + '@stablelib/xchacha20poly1305': 1.0.1 + bech32: 2.0.0 + canonicalize: 2.0.0 + did-resolver: 4.1.0 + elliptic: 6.5.4 + js-sha3: 0.8.0 + multiformats: 9.9.0 + uint8arrays: 3.1.1 + dev: true + + /did-jwt@7.4.5: + resolution: {integrity: sha512-PjUFy/yhYeivNrQI5EaqYvF+cRL0itARQlXPfAnUUcj4tm40fzCU/0yWkhAoAPfM41e8O+QVRqOXwg0cZjlVeg==} + dependencies: + '@noble/ciphers': 0.4.0 + '@noble/curves': 1.2.0 + '@noble/hashes': 1.3.2 + '@scure/base': 1.1.3 + canonicalize: 2.0.0 + did-resolver: 4.1.0 + multibase: 4.0.6 + multiformats: 9.9.0 + uint8arrays: 3.1.1 + dev: true + /did-resolver@4.1.0: resolution: {integrity: sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA==} dev: true @@ -6373,6 +6639,11 @@ packages: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} dev: true + /events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + dev: true + /exec-async@2.2.0: resolution: {integrity: sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==} @@ -6492,6 +6763,15 @@ packages: transitivePeerDependencies: - supports-color + /expo-crypto@12.6.0(expo@48.0.20): + resolution: {integrity: sha512-wSq64eIJxk4lQBidtcW9wNF5AgO/UvV8W8mDhb7bo6P3xH41yvu/P4FcxevQY1taGA8VHD+fO+xQDrhPiHzFqQ==} + peerDependencies: + expo: '*' + dependencies: + base64-js: 1.5.1 + expo: 48.0.20(@babel/core@7.23.0) + dev: true + /expo-file-system@15.2.2(expo@48.0.20): resolution: {integrity: sha512-LFkOLcWwlmnjkURxZ3/0ukS35OswX8iuQknLHRHeyk8mUA8fpRPPelD/a1lS+yclqfqavMJmTXVKM1Nsq5XVMA==} peerDependencies: @@ -7431,6 +7711,15 @@ packages: dependencies: function-bind: 1.1.1 + /hash-base@3.1.0: + resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} + engines: {node: '>=4'} + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + safe-buffer: 5.2.1 + dev: true + /hash.js@1.1.7: resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} dependencies: @@ -7775,6 +8064,14 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + /is-arguments@1.1.1: + resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} dependencies: @@ -7863,6 +8160,13 @@ packages: engines: {node: '>=6'} dev: true + /is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + /is-glob@2.0.1: resolution: {integrity: sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==} engines: {node: '>=0.10.0'} @@ -8635,6 +8939,10 @@ packages: resolution: {integrity: sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg==} dev: false + /js-sha3@0.8.0: + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} + dev: true + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -9072,10 +9380,22 @@ packages: /lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + /lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + dev: true + + /lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + dev: true + /lodash.ismatch@4.4.0: resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} dev: true + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: true + /lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} dev: true @@ -9239,6 +9559,14 @@ packages: dependencies: buffer-alloc: 1.2.0 + /md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + /md5@2.2.1: resolution: {integrity: sha512-PlGG4z5mBANDGCKsYQe0CaUYHdZYZt8ZPZLmEt+Urf0W4GlpTX4HescwHU+dc9+Z/G/vZKYZYFrwgm9VxK6QOQ==} dependencies: @@ -9884,7 +10212,14 @@ packages: /msrcrypto@1.5.8: resolution: {integrity: sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q==} - dev: false + + /multibase@4.0.6: + resolution: {integrity: sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ==} + engines: {node: '>=12.0.0', npm: '>=6.0.0'} + deprecated: This module has been superseded by the multiformats module + dependencies: + '@multiformats/base-x': 4.0.1 + dev: true /multiformats@9.9.0: resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} @@ -11087,6 +11422,19 @@ packages: engines: {node: '>= 0.8'} dev: false + /randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /randomfill@1.0.4: + resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + dependencies: + randombytes: 2.1.0 + safe-buffer: 5.2.1 + dev: true + /range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -11176,7 +11524,6 @@ packages: dependencies: base64-js: 1.5.1 react-native: 0.71.13(@babel/core@7.23.0)(@babel/preset-env@7.22.20)(react@18.2.0) - dev: false /react-native@0.71.13(@babel/core@7.23.0)(@babel/preset-env@7.22.20)(react@18.2.0): resolution: {integrity: sha512-zEa69YQNLdv8Sf5Pn0CNDB1K9eGuNy1KoMNxXlrZ89JZ8d02b5hihZIoOCCIwhH+iPgslYwr3ZoGd3AY6FMrgw==} @@ -11583,6 +11930,13 @@ packages: glob: 10.3.10 dev: true + /ripemd160@2.0.2: + resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + dev: true + /roarr@7.15.1: resolution: {integrity: sha512-0ExL9rjOXeQPvQvQo8IcV8SR2GTXmDr1FQFlY2HiAV+gdVQjaVZNOx9d4FI2RqFFsd0sNsiw2TRS/8RU9g0ZfA==} engines: {node: '>=12.0'} @@ -11780,6 +12134,14 @@ packages: /setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + /sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + /shallow-clone@3.0.1: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} @@ -12024,7 +12386,6 @@ packages: /str2buf@1.3.0: resolution: {integrity: sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA==} - dev: false /stream-buffers@2.2.0: resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==} @@ -13000,6 +13361,16 @@ packages: /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + /util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + dependencies: + inherits: 2.0.4 + is-arguments: 1.1.1 + is-generator-function: 1.0.10 + is-typed-array: 1.1.12 + which-typed-array: 1.1.11 + dev: true + /utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} @@ -13060,6 +13431,15 @@ packages: builtins: 5.0.1 dev: true + /validator@13.11.0: + resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} + engines: {node: '>= 0.10'} + dev: true + + /varint@6.0.0: + resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} + dev: true + /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -13077,6 +13457,14 @@ packages: dependencies: defaults: 1.0.4 + /web-encoding@1.1.5: + resolution: {integrity: sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==} + dependencies: + util: 0.12.5 + optionalDependencies: + '@zxing/text-encoding': 0.9.0 + dev: true + /webcrypto-core@1.7.7: resolution: {integrity: sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g==} dependencies: @@ -13088,7 +13476,6 @@ packages: /webcrypto-shim@0.1.7: resolution: {integrity: sha512-JAvAQR5mRNRxZW2jKigWMjCMkjSdmP5cColRP1U/pTg69VgHXEi1orv5vVpJ55Zc5MIaPc1aaurzd9pjv2bveg==} - dev: false /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -13412,3 +13799,15 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} dev: true + + /z-schema@5.0.5: + resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} + engines: {node: '>=8.0.0'} + hasBin: true + dependencies: + lodash.get: 4.4.2 + lodash.isequal: 4.5.0 + validator: 13.11.0 + optionalDependencies: + commander: 9.5.0 + dev: true From c44107f580744292a987ba2cda5795e443aaa9df Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Thu, 21 Dec 2023 19:09:50 +0100 Subject: [PATCH 06/11] feat: EBSI compatibility --- .../client/lib/__tests__/EBSIE2E.spec.test.ts | 132 ++++++++++++++++++ 1 file changed, 132 insertions(+) create mode 100644 packages/client/lib/__tests__/EBSIE2E.spec.test.ts diff --git a/packages/client/lib/__tests__/EBSIE2E.spec.test.ts b/packages/client/lib/__tests__/EBSIE2E.spec.test.ts new file mode 100644 index 00000000..5af36248 --- /dev/null +++ b/packages/client/lib/__tests__/EBSIE2E.spec.test.ts @@ -0,0 +1,132 @@ +import { Alg, CodeChallengeMethod, Jwt } from '@sphereon/oid4vci-common' +import { toJwk } from '@sphereon/ssi-sdk-ext.key-utils' +import { CredentialMapper } from '@sphereon/ssi-types' +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +//@ts-ignore +import { from } from '@trust/keyto' +import { fetch } from 'cross-fetch' +import debug from 'debug' +import { base64url, importJWK, JWK, SignJWT } from 'jose' +import * as u8a from 'uint8arrays' + +import { OpenID4VCIClient } from '..' + +export const UNIT_TEST_TIMEOUT = 30000; + +const ISSUER_URL = 'https://conformance-test.ebsi.eu/conformance/v3/issuer-mock'; +const AUTH_URL = 'https://conformance-test.ebsi.eu/conformance/v3/auth-mock'; + +const jwk: JWK = { + alg: 'ES256', + use: 'sig', + kty: 'EC', + crv: 'P-256', + x: 'hUWYK06qFvdudydiqnEhVJhZ-73jcLtuzH8kIyNOSHE', + y: 'UZf7oUkJdo65SQekMD5ssiRclEimG2SmlsjXf3QwQJo', + d: 'zDeeo3K0Pk8dofeKcajvJYxMZ1vijx_cVDJQl1IpbAM', +}; + +console.log(`JWK (private/orig): ${JSON.stringify(jwk, null, 2)}`); + +const privateKey = from(jwk, 'jwk').toString('blk', 'private'); +const publicKey = from(jwk, 'jwk').toString('blk', 'public'); +console.log(`Private key: ${privateKey}`); +console.log(`Public key: ${publicKey}`); +console.log(`Private key (b64): ${base64url.encode(u8a.fromString(privateKey, 'base16'))}`); +console.log(`JWK (private 2) ${JSON.stringify(toJwk(privateKey, 'Secp256r1', { isPrivateKey: true }))}`); +console.log(`JWK (public 2) ${JSON.stringify(toJwk(publicKey, 'Secp256r1', { isPrivateKey: false }))}`); + +// const DID_METHOD = 'did:key' +const DID = + 'did:key:z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrm54tL4pRrDDhR1QJ5RHPMXUq5MzYpZL2k35vya5eMiNxschNy9AJ74CC3MmcYiZJGZfyhWQ6qDgTVcDSHdquwPYvLDut383JbrgYdZYYSC2merTMgmQtUi3huYhaky1qE'; +const DID_URL_ENCODED = + 'did%3Akey%3Az2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrm54tL4pRrDDhR1QJ5RHPMXUq5MzYpZL2k35vya5eMiNxschNy9AJ74CC3MmcYiZJGZfyhWQ6qDgTVcDSHdquwPYvLDut383JbrgYdZYYSC2merTMgmQtUi3huYhaky1qE'; +// const PRIVATE_KEY_HEX = '7dd923e40f4615ac496119f7e793cc2899e99b64b88ca8603db986700089532b' + +// const PUBLIC_KEY_HEX = +// '04a23cb4c83901acc2eb0f852599610de0caeac260bf8ed05e7f902eaac0f9c8d74dd4841b94d13424d32af8ec0e9976db9abfa7e3a59e10d565c5d4d901b4be63' + +// pub hex: 35e03477cb29f3ac518770dccd4e26e703cd21b9741c24b038170c377b0d99d9 +// priv hex: 913466d1a38d1d8c0d3c0fb0fc3b633075085a31372bbd2a8022215a88d9d1e5 +// const did = `did:key:z6Mki5ZwZKN1dBQprfJTikUvkDxrHijiiQngkWviMF5gw2Hv`; +const kid = `${DID}#z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrm54tL4pRrDDhR1QJ5RHPMXUq5MzYpZL2k35vya5eMiNxschNy9AJ74CC3MmcYiZJGZfyhWQ6qDgTVcDSHdquwPYvLDut383JbrgYdZYYSC2merTMgmQtUi3huYhaky1qE`; + +// const jw = jose.importKey() +describe('OID4VCI-Client using Sphereon issuer should', () => { + async function test(credentialType: 'CTWalletCrossPreAuthorised' | 'CTWalletCrossInTime') { + debug.enable('*'); + const offer = await getCredentialOffer(credentialType); + const client = await OpenID4VCIClient.fromURI({ + uri: offer, + kid, + alg: Alg.ES256, + clientId: DID_URL_ENCODED + }); + expect(client.credentialOffer).toBeDefined(); + expect(client.endpointMetadata).toBeDefined(); + expect(client.getCredentialEndpoint()).toEqual(`${ISSUER_URL}/credential`); + expect(client.getAccessTokenEndpoint()).toEqual(`${AUTH_URL}/token`); + + if (credentialType !== 'CTWalletCrossPreAuthorised') { + const url = client.createAuthorizationRequestUrl({redirectUri: 'openid4vc%3A', codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', codeChallengeMethod: CodeChallengeMethod.SHA256}) + const result = await fetch(url) + console.log(result.text()) + } + + const accessToken = await client.acquireAccessToken({ pin: '0891' }); + // console.log(accessToken); + expect(accessToken).toMatchObject({ + expires_in: 86400, + // scope: 'GuestCredential', + token_type: 'Bearer', + }); + + const format = 'jwt_vc'; + const credentialResponse = await client.acquireCredentials({ + credentialTypes: client.getCredentialOfferTypes()[0], + format, + proofCallbacks: { + signCallback: proofOfPossessionCallbackFunction, + }, + kid, + }); + console.log(JSON.stringify(credentialResponse, null, 2)) + expect(credentialResponse.credential).toBeDefined(); + const wrappedVC = CredentialMapper.toWrappedVerifiableCredential(credentialResponse.credential!); + expect(format.startsWith(wrappedVC.format)).toEqual(true); + } + + it( + 'succeed in a full flow with the client using OpenID4VCI version 11 and jwt_vc_json', + async () => { + await test('CTWalletCrossPreAuthorised') + // await test('CTWalletCrossInTime'); + }, + UNIT_TEST_TIMEOUT, + ); +}); + +async function getCredentialOffer(credentialType: 'CTWalletCrossPreAuthorised' | 'CTWalletCrossInTime'): Promise { + const credentialOffer = await fetch( + `https://conformance-test.ebsi.eu/conformance/v3/issuer-mock/initiate-credential-offer?credential_type=${credentialType}&client_id=${DID_URL_ENCODED}&credential_offer_endpoint=openid-credential-offer%3A%2F%2F`, + { + method: 'GET', + headers: { + Accept: 'application/json', + 'Content-Type': 'application/json', + }, + }, + ); + + return await credentialOffer.text(); +} + +async function proofOfPossessionCallbackFunction(args: Jwt, kid?: string): Promise { + const importedJwk = await importJWK(jwk); + return await new SignJWT({ ...args.payload }) + .setProtectedHeader({ ...args.header, kid: kid! }) + .setIssuer(DID) + .setIssuedAt() + .setExpirationTime('2m') + .sign(importedJwk); +} From a504fb24a8d6127624e3e5497f1a6633e816253d Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Thu, 21 Dec 2023 20:52:01 +0100 Subject: [PATCH 07/11] chore: test fix --- packages/client/lib/OpenID4VCIClient.ts | 11 +++--- .../client/lib/__tests__/EBSIE2E.spec.test.ts | 34 +++++++++++-------- .../lib/__tests__/OpenID4VCIClient.spec.ts | 5 +++ .../lib/functions/IssuerMetadataUtils.ts | 2 +- 4 files changed, 30 insertions(+), 22 deletions(-) diff --git a/packages/client/lib/OpenID4VCIClient.ts b/packages/client/lib/OpenID4VCIClient.ts index d13e8040..2d7a8698 100644 --- a/packages/client/lib/OpenID4VCIClient.ts +++ b/packages/client/lib/OpenID4VCIClient.ts @@ -60,7 +60,7 @@ export class OpenID4VCIClient { clientId, kid, alg, - credentialIssuer + credentialIssuer, }: { credentialOffer?: CredentialOfferRequestWithBaseUrl; kid?: string; @@ -69,12 +69,11 @@ export class OpenID4VCIClient { credentialIssuer?: string; }) { this._credentialOffer = credentialOffer; - const issuer = - credentialIssuer ?? (credentialOffer ? getIssuerFromCredentialOfferPayload(credentialOffer.credential_offer) : undefined); + const issuer = credentialIssuer ?? (credentialOffer ? getIssuerFromCredentialOfferPayload(credentialOffer.credential_offer) : undefined); if (!issuer) { - throw Error('No credential issuer supplied or deduced from offer') + throw Error('No credential issuer supplied or deduced from offer'); } - this._credentialIssuer = issuer + this._credentialIssuer = issuer; this._kid = kid; this._alg = alg; this._clientId = clientId; @@ -154,7 +153,7 @@ export class OpenID4VCIClient { const creds = this.credentialOffer.credential_offer.credentials; authorizationDetails = creds - .flatMap((cred) => (typeof cred === 'string' ? this.getCredentialsSupported(true) : cred as CredentialSupported)) + .flatMap((cred) => (typeof cred === 'string' ? this.getCredentialsSupported(true) : (cred as CredentialSupported))) .map((cred) => { return { ...cred, diff --git a/packages/client/lib/__tests__/EBSIE2E.spec.test.ts b/packages/client/lib/__tests__/EBSIE2E.spec.test.ts index 5af36248..2ee479b7 100644 --- a/packages/client/lib/__tests__/EBSIE2E.spec.test.ts +++ b/packages/client/lib/__tests__/EBSIE2E.spec.test.ts @@ -1,15 +1,15 @@ -import { Alg, CodeChallengeMethod, Jwt } from '@sphereon/oid4vci-common' -import { toJwk } from '@sphereon/ssi-sdk-ext.key-utils' -import { CredentialMapper } from '@sphereon/ssi-types' +import { Alg, CodeChallengeMethod, Jwt } from '@sphereon/oid4vci-common'; +import { toJwk } from '@sphereon/ssi-sdk-ext.key-utils'; +import { CredentialMapper } from '@sphereon/ssi-types'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment //@ts-ignore -import { from } from '@trust/keyto' -import { fetch } from 'cross-fetch' -import debug from 'debug' -import { base64url, importJWK, JWK, SignJWT } from 'jose' -import * as u8a from 'uint8arrays' +import { from } from '@trust/keyto'; +import { fetch } from 'cross-fetch'; +import debug from 'debug'; +import { base64url, importJWK, JWK, SignJWT } from 'jose'; +import * as u8a from 'uint8arrays'; -import { OpenID4VCIClient } from '..' +import { OpenID4VCIClient } from '..'; export const UNIT_TEST_TIMEOUT = 30000; @@ -60,7 +60,7 @@ describe('OID4VCI-Client using Sphereon issuer should', () => { uri: offer, kid, alg: Alg.ES256, - clientId: DID_URL_ENCODED + clientId: DID_URL_ENCODED, }); expect(client.credentialOffer).toBeDefined(); expect(client.endpointMetadata).toBeDefined(); @@ -68,9 +68,13 @@ describe('OID4VCI-Client using Sphereon issuer should', () => { expect(client.getAccessTokenEndpoint()).toEqual(`${AUTH_URL}/token`); if (credentialType !== 'CTWalletCrossPreAuthorised') { - const url = client.createAuthorizationRequestUrl({redirectUri: 'openid4vc%3A', codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', codeChallengeMethod: CodeChallengeMethod.SHA256}) - const result = await fetch(url) - console.log(result.text()) + const url = client.createAuthorizationRequestUrl({ + redirectUri: 'openid4vc%3A', + codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', + codeChallengeMethod: CodeChallengeMethod.SHA256, + }); + const result = await fetch(url); + console.log(result.text()); } const accessToken = await client.acquireAccessToken({ pin: '0891' }); @@ -90,7 +94,7 @@ describe('OID4VCI-Client using Sphereon issuer should', () => { }, kid, }); - console.log(JSON.stringify(credentialResponse, null, 2)) + console.log(JSON.stringify(credentialResponse, null, 2)); expect(credentialResponse.credential).toBeDefined(); const wrappedVC = CredentialMapper.toWrappedVerifiableCredential(credentialResponse.credential!); expect(format.startsWith(wrappedVC.format)).toEqual(true); @@ -99,7 +103,7 @@ describe('OID4VCI-Client using Sphereon issuer should', () => { it( 'succeed in a full flow with the client using OpenID4VCI version 11 and jwt_vc_json', async () => { - await test('CTWalletCrossPreAuthorised') + await test('CTWalletCrossPreAuthorised'); // await test('CTWalletCrossInTime'); }, UNIT_TEST_TIMEOUT, diff --git a/packages/client/lib/__tests__/OpenID4VCIClient.spec.ts b/packages/client/lib/__tests__/OpenID4VCIClient.spec.ts index d29100bd..97a575db 100644 --- a/packages/client/lib/__tests__/OpenID4VCIClient.spec.ts +++ b/packages/client/lib/__tests__/OpenID4VCIClient.spec.ts @@ -68,6 +68,11 @@ describe('OpenID4VCIClient should', () => { expect(scope?.[0]).toBe('openid'); }); it('throw an error if no scope and no authorization_details is provided', async () => { + nock(MOCK_URL).get(/.*/).reply(200, {}); + nock(MOCK_URL).get(WellKnownEndpoints.OAUTH_AS).reply(404, {}); + nock(MOCK_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(404, {}); + // Use a client with issuer only to trigger the error + client = await OpenID4VCIClient.fromCredentialIssuer({ credentialIssuer: 'https://server.example.com' }); // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore client._endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`; diff --git a/packages/common/lib/functions/IssuerMetadataUtils.ts b/packages/common/lib/functions/IssuerMetadataUtils.ts index 5bf406cc..6d6d730a 100644 --- a/packages/common/lib/functions/IssuerMetadataUtils.ts +++ b/packages/common/lib/functions/IssuerMetadataUtils.ts @@ -43,7 +43,7 @@ export function getSupportedCredential(opts?: { } const { version, types } = opts ?? { version: OpenId4VCIVersion.VER_1_0_11 }; if (version === OpenId4VCIVersion.VER_1_0_08 || !Array.isArray(issuerMetadata.credentials_supported)) { - credentialsSupported = credentialsSupportedV8ToV11((issuerMetadata as IssuerMetadataV1_0_08).credentials_supported); + credentialsSupported = credentialsSupportedV8ToV11((issuerMetadata as IssuerMetadataV1_0_08).credentials_supported ?? {}); } else { credentialsSupported = (issuerMetadata as CredentialIssuerMetadata).credentials_supported; } From fc8c1519ee5e45f12bc40327bedb0a0a79bbe867 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Thu, 21 Dec 2023 22:17:42 +0100 Subject: [PATCH 08/11] chore: issuer type fix --- packages/issuer/lib/VcIssuer.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/packages/issuer/lib/VcIssuer.ts b/packages/issuer/lib/VcIssuer.ts index e172d7a1..de32b1a7 100644 --- a/packages/issuer/lib/VcIssuer.ts +++ b/packages/issuer/lib/VcIssuer.ts @@ -29,6 +29,7 @@ import { OID4VCICredentialFormat, OpenId4VCIVersion, QRCodeOpts, + SdJwtVcCredentialDefinition, TokenErrorResponse, toUniformCredentialOfferRequest, TYP_ERROR, @@ -94,7 +95,7 @@ export class VcIssuer { public async createCredentialOfferURI(opts: { grants?: Grant credentials?: (CredentialOfferFormat | string)[] - credentialDefinition?: JsonLdIssuerCredentialDefinition + credentialDefinition?: JsonLdIssuerCredentialDefinition | SdJwtVcCredentialDefinition credentialOfferUri?: string credentialDataSupplierInput?: CredentialDataSupplierInput // Optional storage that can help the credential Data Supplier. For instance to store credential input data during offer creation, if no additional data can be supplied later on baseUri?: string From 7eb9494636fa1043016fb2e25fade2efbf46dd81 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Fri, 22 Dec 2023 12:08:38 +0100 Subject: [PATCH 09/11] chore: test fixes --- packages/client/lib/__tests__/EBSIE2E.spec.test.ts | 14 ++++++++------ .../lib/__tests__/IssuerTokenServer.spec.ts | 4 ++-- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/packages/client/lib/__tests__/EBSIE2E.spec.test.ts b/packages/client/lib/__tests__/EBSIE2E.spec.test.ts index 2ee479b7..400b2d1e 100644 --- a/packages/client/lib/__tests__/EBSIE2E.spec.test.ts +++ b/packages/client/lib/__tests__/EBSIE2E.spec.test.ts @@ -53,7 +53,7 @@ const kid = `${DID}#z2dmzD81cgPx8Vki7JbuuMmFYrWPgYoytykUZ3eyqht1j9Kbrm54tL4pRrDD // const jw = jose.importKey() describe('OID4VCI-Client using Sphereon issuer should', () => { - async function test(credentialType: 'CTWalletCrossPreAuthorised' | 'CTWalletCrossInTime') { + async function test(credentialType: 'CTWalletCrossPreAuthorisedInTime' | 'CTWalletCrossAuthorisedInTime') { debug.enable('*'); const offer = await getCredentialOffer(credentialType); const client = await OpenID4VCIClient.fromURI({ @@ -67,7 +67,7 @@ describe('OID4VCI-Client using Sphereon issuer should', () => { expect(client.getCredentialEndpoint()).toEqual(`${ISSUER_URL}/credential`); expect(client.getAccessTokenEndpoint()).toEqual(`${AUTH_URL}/token`); - if (credentialType !== 'CTWalletCrossPreAuthorised') { + if (credentialType !== 'CTWalletCrossPreAuthorisedInTime') { const url = client.createAuthorizationRequestUrl({ redirectUri: 'openid4vc%3A', codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', @@ -100,17 +100,19 @@ describe('OID4VCI-Client using Sphereon issuer should', () => { expect(format.startsWith(wrappedVC.format)).toEqual(true); } - it( + // Current conformance tests is not stable as changes are being applied it seems + + it.skip( 'succeed in a full flow with the client using OpenID4VCI version 11 and jwt_vc_json', async () => { - await test('CTWalletCrossPreAuthorised'); - // await test('CTWalletCrossInTime'); + await test('CTWalletCrossPreAuthorisedInTime'); + // await test('CTWalletCrossAuthorisedInTime'); }, UNIT_TEST_TIMEOUT, ); }); -async function getCredentialOffer(credentialType: 'CTWalletCrossPreAuthorised' | 'CTWalletCrossInTime'): Promise { +async function getCredentialOffer(credentialType: 'CTWalletCrossPreAuthorisedInTime' | 'CTWalletCrossAuthorisedInTime'): Promise { const credentialOffer = await fetch( `https://conformance-test.ebsi.eu/conformance/v3/issuer-mock/initiate-credential-offer?credential_type=${credentialType}&client_id=${DID_URL_ENCODED}&credential_offer_endpoint=openid-credential-offer%3A%2F%2F`, { diff --git a/packages/issuer-rest/lib/__tests__/IssuerTokenServer.spec.ts b/packages/issuer-rest/lib/__tests__/IssuerTokenServer.spec.ts index f334ead8..fde3285a 100644 --- a/packages/issuer-rest/lib/__tests__/IssuerTokenServer.spec.ts +++ b/packages/issuer-rest/lib/__tests__/IssuerTokenServer.spec.ts @@ -137,12 +137,12 @@ describe('OID4VCIServer', () => { expressSupport = ExpressBuilder.fromServerOpts({ startListening: false, - port: 5000, + port: 9000, hostname: '0.0.0.0', }).build({ startListening: false }) const vcIssuerServer = new OID4VCIServer(expressSupport, { issuer: vcIssuer, - baseUrl: 'http://localhost:5000', + baseUrl: 'http://localhost:9000', endpointOpts: { tokenEndpointOpts: { accessTokenSignerCallback: signerCallback, From 40c2908cd1ed2f1099d918faba72be2a3fefbe96 Mon Sep 17 00:00:00 2001 From: Timo Glastra Date: Sat, 23 Dec 2023 13:32:52 +0700 Subject: [PATCH 10/11] update to released ssi-types Signed-off-by: Timo Glastra --- packages/callback-example/package.json | 2 +- packages/client/package.json | 2 +- packages/common/lib/types/QRCode.types.ts | 4 +-- packages/common/package.json | 2 +- packages/issuer-rest/package.json | 4 +-- packages/issuer/package.json | 2 +- pnpm-lock.yaml | 43 +++++++++++++---------- 7 files changed, 33 insertions(+), 26 deletions(-) diff --git a/packages/callback-example/package.json b/packages/callback-example/package.json index 7373224e..8c814fb8 100644 --- a/packages/callback-example/package.json +++ b/packages/callback-example/package.json @@ -18,7 +18,7 @@ "@sphereon/oid4vci-client": "workspace:*", "@sphereon/oid4vci-common": "workspace:*", "@sphereon/oid4vci-issuer": "workspace:*", - "@sphereon/ssi-types": "0.17.2", + "@sphereon/ssi-types": "0.17.6-unstable.69", "jose": "^4.10.0" }, "devDependencies": { diff --git a/packages/client/package.json b/packages/client/package.json index 1d1c5a01..8b339c6b 100644 --- a/packages/client/package.json +++ b/packages/client/package.json @@ -16,7 +16,7 @@ }, "dependencies": { "@sphereon/oid4vci-common": "workspace:*", - "@sphereon/ssi-types": "0.17.2", + "@sphereon/ssi-types": "0.17.6-unstable.69", "cross-fetch": "^3.1.8", "debug": "^4.3.4" }, diff --git a/packages/common/lib/types/QRCode.types.ts b/packages/common/lib/types/QRCode.types.ts index 99deb63b..16280dcb 100644 --- a/packages/common/lib/types/QRCode.types.ts +++ b/packages/common/lib/types/QRCode.types.ts @@ -60,7 +60,7 @@ export interface ComponentOptions { */ protectors?: boolean; }; -}; +} export interface QRCodeOpts { /** @@ -224,4 +224,4 @@ export interface QRCodeOpts { * @deafultValue 0.4 */ dotScale?: number; -}; +} diff --git a/packages/common/package.json b/packages/common/package.json index ff5ff6bd..a08104fd 100644 --- a/packages/common/package.json +++ b/packages/common/package.json @@ -10,7 +10,7 @@ "build:clean": "tsc --build --clean && tsc --build" }, "dependencies": { - "@sphereon/ssi-types": "0.17.2", + "@sphereon/ssi-types": "0.17.6-unstable.69", "cross-fetch": "^3.1.8", "jwt-decode": "^3.1.2" }, diff --git a/packages/issuer-rest/package.json b/packages/issuer-rest/package.json index 3f1e5ede..02270a8a 100644 --- a/packages/issuer-rest/package.json +++ b/packages/issuer-rest/package.json @@ -13,8 +13,8 @@ "dependencies": { "@sphereon/oid4vci-common": "workspace:*", "@sphereon/oid4vci-issuer": "workspace:*", - "@sphereon/ssi-express-support": "0.17.2", - "@sphereon/ssi-types": "0.17.2", + "@sphereon/ssi-express-support": "0.17.6-unstable.69", + "@sphereon/ssi-types": "0.17.6-unstable.69", "body-parser": "^1.20.2", "cookie-parser": "^1.4.6", "cors": "^2.8.5", diff --git a/packages/issuer/package.json b/packages/issuer/package.json index 5a68335c..61fae484 100644 --- a/packages/issuer/package.json +++ b/packages/issuer/package.json @@ -11,7 +11,7 @@ }, "dependencies": { "@sphereon/oid4vci-common": "workspace:*", - "@sphereon/ssi-types": "0.17.2", + "@sphereon/ssi-types": "0.17.6-unstable.69", "uuid": "^9.0.0" }, "peerDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index cbe61181..ffc1e70b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -75,8 +75,8 @@ importers: specifier: workspace:* version: link:../issuer '@sphereon/ssi-types': - specifier: 0.17.2 - version: 0.17.2 + specifier: 0.17.6-unstable.69 + version: 0.17.6-unstable.69 jose: specifier: ^4.10.0 version: 4.14.6 @@ -118,8 +118,8 @@ importers: specifier: workspace:* version: link:../common '@sphereon/ssi-types': - specifier: 0.17.2 - version: 0.17.2 + specifier: 0.17.6-unstable.69 + version: 0.17.6-unstable.69 cross-fetch: specifier: ^3.1.8 version: 3.1.8 @@ -200,8 +200,8 @@ importers: packages/common: dependencies: '@sphereon/ssi-types': - specifier: 0.17.2 - version: 0.17.2 + specifier: 0.17.6-unstable.69 + version: 0.17.6-unstable.69 cross-fetch: specifier: ^3.1.8 version: 3.1.8 @@ -222,8 +222,8 @@ importers: specifier: workspace:* version: link:../common '@sphereon/ssi-types': - specifier: 0.17.2 - version: 0.17.2 + specifier: 0.17.6-unstable.69 + version: 0.17.6-unstable.69 awesome-qr: specifier: ^2.1.5-rc.0 version: 2.1.5-rc.0 @@ -256,11 +256,11 @@ importers: specifier: workspace:* version: link:../issuer '@sphereon/ssi-express-support': - specifier: 0.17.2 - version: 0.17.2 + specifier: 0.17.6-unstable.69 + version: 0.17.6-unstable.69 '@sphereon/ssi-types': - specifier: 0.17.2 - version: 0.17.2 + specifier: 0.17.6-unstable.69 + version: 0.17.6-unstable.69 body-parser: specifier: ^1.20.2 version: 1.20.2 @@ -3349,6 +3349,12 @@ packages: /@react-native/polyfills@2.0.0: resolution: {integrity: sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ==} + /@sd-jwt/core@0.1.2-alpha.0: + resolution: {integrity: sha512-x4MVXar6WmPauZDRJ3aHwaY8o/bHzN77Ts7o43JKuuqIBFjPgAcSlRtd/Xk1rWhazFai4MCIwJDSQ1OQRJtNug==} + dependencies: + buffer: 6.0.3 + dev: false + /@segment/loosely-validate-event@2.0.0: resolution: {integrity: sha512-ZMCSfztDBqwotkl848ODgVcAmN4OItEWDCkshcKz0/W6gGSQayuuCtWV/MlodFivAZD793d6UgANd6wCXUfrIw==} dependencies: @@ -3412,8 +3418,8 @@ packages: dependencies: '@sinonjs/commons': 3.0.0 - /@sphereon/ssi-express-support@0.17.2: - resolution: {integrity: sha512-OrLC7YAelpUmCIzPRgHM97HBNFqDoSdJNNssstS6Ho0ZXswq4fsPDm+h49+//ogp1ERbuOl9Ywqhp+3DdLZCPA==} + /@sphereon/ssi-express-support@0.17.6-unstable.69: + resolution: {integrity: sha512-IpiiW6KPv5zjvlCCxyw4S093WL4na6zvJjoWI26te04Y4T1yYF3FfaGdcA+BAQl4URvvcp09mzay2Z8RwmDLSA==} peerDependencies: '@noble/hashes': 1.2.0 passport-azure-ad: ^4.3.5 @@ -3443,9 +3449,10 @@ packages: - supports-color dev: false - /@sphereon/ssi-types@0.17.2: - resolution: {integrity: sha512-Qo1dkISavtPIe1WKZXZGyHvquoUvdUlDI0GLzb21clKFPuxbawXdlxpCqOh6NCNRfX7ohEeCUQdEA1PNBlnKYA==} + /@sphereon/ssi-types@0.17.6-unstable.69: + resolution: {integrity: sha512-VwjVd6XhoV5QecWcRh0RpBf5N324tfhYcQrVk9se3brqMNMauZTBbhUhk+QLdwFd+u/1+IfEWnS28HyIU7lXHQ==} dependencies: + '@sd-jwt/core': 0.1.2-alpha.0 jwt-decode: 3.1.2 dev: false @@ -4964,7 +4971,7 @@ packages: minipass-pipeline: 1.2.4 p-map: 4.0.0 ssri: 10.0.5 - tar: 6.2.0 + tar: 6.1.11 unique-filename: 3.0.0 dev: true @@ -10044,7 +10051,7 @@ packages: npmlog: 6.0.2 rimraf: 3.0.2 semver: 7.5.4 - tar: 6.2.0 + tar: 6.1.11 which: 2.0.2 transitivePeerDependencies: - supports-color From 7577e3d8a4818fe0955fce944220d6fb415a58a7 Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Wed, 10 Jan 2024 13:50:10 +0100 Subject: [PATCH 11/11] feat: Add EBSI support --- .../__tests__/CredentialRequestClient.spec.ts | 9 +- .../lib/__tests__/MetadataClient.spec.ts | 5 +- packages/client/lib/__tests__/SdJwt.spec.ts | 2 +- packages/issuer/lib/VcIssuer.ts | 3 +- pnpm-lock.yaml | 413 +++++++++++++++++- 5 files changed, 420 insertions(+), 12 deletions(-) diff --git a/packages/client/lib/__tests__/CredentialRequestClient.spec.ts b/packages/client/lib/__tests__/CredentialRequestClient.spec.ts index dc87a1d4..de8d05e5 100644 --- a/packages/client/lib/__tests__/CredentialRequestClient.spec.ts +++ b/packages/client/lib/__tests__/CredentialRequestClient.spec.ts @@ -157,8 +157,15 @@ describe('Credential Request Client ', () => { }); describe('Credential Request Client with Walt.id ', () => { - it('should have correct metadata endpoints', async function () { + beforeAll(() => { + nock.cleanAll(); + }); + + afterEach(() => { nock.cleanAll(); + }); + it('should have correct metadata endpoints', async function () { + // nock.cleanAll(); const WALT_IRR_URI = 'openid-initiate-issuance://?issuer=https%3A%2F%2Fjff.walt.id%2Fissuer-api%2Foidc%2F&credential_type=OpenBadgeCredential&pre-authorized_code=eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJhOTUyZjUxNi1jYWVmLTQ4YjMtODIxYy00OTRkYzgyNjljZjAiLCJwcmUtYXV0aG9yaXplZCI6dHJ1ZX0.YE5DlalcLC2ChGEg47CQDaN1gTxbaQqSclIVqsSAUHE&user_pin_required=false'; const credentialOffer = await CredentialOfferClient.fromURI(WALT_IRR_URI); diff --git a/packages/client/lib/__tests__/MetadataClient.spec.ts b/packages/client/lib/__tests__/MetadataClient.spec.ts index e278a525..f1afbd7d 100644 --- a/packages/client/lib/__tests__/MetadataClient.spec.ts +++ b/packages/client/lib/__tests__/MetadataClient.spec.ts @@ -1,4 +1,6 @@ import { getIssuerFromCredentialOfferPayload, WellKnownEndpoints } from '@sphereon/oid4vci-common'; +// eslint-disable-next-line @typescript-eslint/ban-ts-comment +// @ts-ignore import nock from 'nock'; import { CredentialOfferClient } from '../CredentialOfferClient'; @@ -211,7 +213,8 @@ describe('Metadataclient with Walt-id should', () => { }); }); -describe('Metadataclient with SpruceId should', () => { +// Spruce gives back 404's these days, so test is disabled +describe.skip('Metadataclient with SpruceId should', () => { beforeAll(() => { nock.cleanAll(); }); diff --git a/packages/client/lib/__tests__/SdJwt.spec.ts b/packages/client/lib/__tests__/SdJwt.spec.ts index e3f8311d..061532e9 100644 --- a/packages/client/lib/__tests__/SdJwt.spec.ts +++ b/packages/client/lib/__tests__/SdJwt.spec.ts @@ -82,7 +82,7 @@ describe('sd-jwt vc', () => { uri: offerUri.uri, }); - expect(client.credentialOffer.credential_offer).toEqual({ + expect(client.credentialOffer?.credential_offer).toEqual({ credential_issuer: 'https://example.com', credentials: ['SdJwtCredentialId'], grants: { diff --git a/packages/issuer/lib/VcIssuer.ts b/packages/issuer/lib/VcIssuer.ts index b7c0ea9c..a2447deb 100644 --- a/packages/issuer/lib/VcIssuer.ts +++ b/packages/issuer/lib/VcIssuer.ts @@ -29,7 +29,6 @@ import { OID4VCICredentialFormat, OpenId4VCIVersion, QRCodeOpts, - SdJwtVcCredentialDefinition, TokenErrorResponse, toUniformCredentialOfferRequest, TYP_ERROR, @@ -95,7 +94,7 @@ export class VcIssuer { public async createCredentialOfferURI(opts: { grants?: Grant credentials?: (CredentialOfferFormat | string)[] - credentialDefinition?: JsonLdIssuerCredentialDefinition | SdJwtVcCredentialDefinition + credentialDefinition?: JsonLdIssuerCredentialDefinition credentialOfferUri?: string credentialDataSupplierInput?: CredentialDataSupplierInput // Optional storage that can help the credential Data Supplier. For instance to store credential input data during offer creation, if no additional data can be supplied later on baseUri?: string diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ffc1e70b..daa77d7b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -127,9 +127,15 @@ importers: specifier: ^4.3.4 version: 4.3.4 devDependencies: + '@sphereon/ssi-sdk-ext.key-utils': + specifier: ^0.15.1-next.7 + version: 0.15.1-unstable.11(expo-crypto@12.6.0)(expo@48.0.20)(msrcrypto@1.5.8)(react-native-securerandom@1.0.1) '@transmute/did-key.js': specifier: ^0.3.0-unstable.10 version: 0.3.0-unstable.10 + '@trust/keyto': + specifier: ^2.0.0-alpha1 + version: 2.0.0-alpha1 '@types/jest': specifier: ^29.5.3 version: 29.5.5 @@ -1969,6 +1975,23 @@ packages: engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dev: true + /@ethersproject/bytes@5.7.0: + resolution: {integrity: sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==} + dependencies: + '@ethersproject/logger': 5.7.0 + dev: true + + /@ethersproject/logger@5.7.0: + resolution: {integrity: sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==} + dev: true + + /@ethersproject/random@5.7.0: + resolution: {integrity: sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==} + dependencies: + '@ethersproject/bytes': 5.7.0 + '@ethersproject/logger': 5.7.0 + dev: true + /@expo/bunyan@4.0.0: resolution: {integrity: sha512-Ydf4LidRB/EBI+YrB+cVLqIseiRfjUI/AeHBgjGMtq3GroraDu81OV7zqophRgupngoL3iS3JUMDMnxO7g39qA==} engines: {'0': node >=0.10.0} @@ -2770,10 +2793,29 @@ packages: dev: true optional: true + /@multiformats/base-x@4.0.1: + resolution: {integrity: sha512-eMk0b9ReBbV23xXU693TAIrLyeO5iTgBZGSJfpqriG8UkYvr/hC9u9pyMlAakDNHWmbhMZCDs6KQO0jzKD8OTw==} + dev: true + + /@noble/ciphers@0.4.1: + resolution: {integrity: sha512-QCOA9cgf3Rc33owG0AYBB9wszz+Ul2kramWN8tXG44Gyciud/tbkEqvxRF/IpqQaBpRBNi9f4jdNxqB2CQCIXg==} + dev: true + + /@noble/curves@1.3.0: + resolution: {integrity: sha512-t01iSXPuN+Eqzb4eBX0S5oubSqXbK/xXa1Ne18Hj8f9pStxztHCE2gfboSp/dZRLSqfuLpRK2nDXDK+W9puocA==} + dependencies: + '@noble/hashes': 1.3.3 + dev: true + /@noble/ed25519@1.7.3: resolution: {integrity: sha512-iR8GBkDt0Q3GyaVcIu7mSsVIqnFbkbRzGLWlvhwunacoLwt4J3swfKhfaM6rN6WY+TBGoYT1GtT1mIh2/jGbRQ==} dev: false + /@noble/hashes@1.3.3: + resolution: {integrity: sha512-V7/fPHgl+jsVPXqqeOzT8egNj2iBIVt+ECeMMG8TdcnTikP3oaBtUVqpT/gYCR68aEBJSF+XbYUxStjbFMqIIA==} + engines: {node: '>= 16'} + dev: true + /@nodelib/fs.scandir@2.1.5: resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -3349,6 +3391,10 @@ packages: /@react-native/polyfills@2.0.0: resolution: {integrity: sha512-K0aGNn1TjalKj+65D7ycc1//H9roAQ51GJVk5ZJQFb2teECGmzd86bYDC0aYdbRf7gtovescq4Zt6FR0tgXiHQ==} + /@scure/base@1.1.5: + resolution: {integrity: sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==} + dev: true + /@sd-jwt/core@0.1.2-alpha.0: resolution: {integrity: sha512-x4MVXar6WmPauZDRJ3aHwaY8o/bHzN77Ts7o43JKuuqIBFjPgAcSlRtd/Xk1rWhazFai4MCIwJDSQ1OQRJtNug==} dependencies: @@ -3418,6 +3464,33 @@ packages: dependencies: '@sinonjs/commons': 3.0.0 + /@sphereon/isomorphic-webcrypto@2.4.0-unstable.4(expo-crypto@12.6.0)(expo@48.0.20)(msrcrypto@1.5.8)(react-native-securerandom@1.0.1): + resolution: {integrity: sha512-7i9GBta0yji3Z5ocyk82fXpqrV/swe7hXZVfVzOXRaGtTUNd+y8W/3cpHRQC2S4UEO/5N3lX7+B6qUunK9wS/Q==} + peerDependencies: + expo: '*' + expo-crypto: '*' + msrcrypto: ^1.5.8 + react-native-securerandom: ^1.0.1 + dependencies: + '@peculiar/webcrypto': 1.4.3 + asmcrypto.js: 2.3.2 + b64-lite: 1.4.0 + b64u-lite: 1.1.0 + cipher-base: 1.0.4 + create-hash: 1.2.0 + expo: 48.0.20(@babel/core@7.23.0) + expo-crypto: 12.6.0(expo@48.0.20) + inherits: 2.0.4 + md5.js: 1.3.5 + msrcrypto: 1.5.8 + randomfill: 1.0.4 + react-native-securerandom: 1.0.1(react-native@0.71.13) + ripemd160: 2.0.2 + sha.js: 2.4.11 + str2buf: 1.3.0 + webcrypto-shim: 0.1.7 + dev: true + /@sphereon/ssi-express-support@0.17.6-unstable.69: resolution: {integrity: sha512-IpiiW6KPv5zjvlCCxyw4S093WL4na6zvJjoWI26te04Y4T1yYF3FfaGdcA+BAQl4URvvcp09mzay2Z8RwmDLSA==} peerDependencies: @@ -3449,6 +3522,32 @@ packages: - supports-color dev: false + /@sphereon/ssi-sdk-ext.key-utils@0.15.1-unstable.11(expo-crypto@12.6.0)(expo@48.0.20)(msrcrypto@1.5.8)(react-native-securerandom@1.0.1): + resolution: {integrity: sha512-F0vR49mjSJ5Tm5sBvq6HduyAFoPbViTzRBG4J7NeUCubT3R35Jmjr2kWDrp8B1vbyQrCoBi5/TE5g30H2Hd4gA==} + dependencies: + '@ethersproject/random': 5.7.0 + '@sphereon/isomorphic-webcrypto': 2.4.0-unstable.4(expo-crypto@12.6.0)(expo@48.0.20)(msrcrypto@1.5.8)(react-native-securerandom@1.0.1) + '@stablelib/ed25519': 1.0.3 + '@stablelib/sha256': 1.0.1 + '@stablelib/sha512': 1.0.1 + '@veramo/core': 4.2.0 + base64url: 3.0.1 + debug: 4.3.4 + did-resolver: 4.1.0 + elliptic: 6.5.4 + lodash.isplainobject: 4.0.6 + multiformats: 9.9.0 + uint8arrays: 3.1.1 + varint: 6.0.0 + web-encoding: 1.1.5 + transitivePeerDependencies: + - expo + - expo-crypto + - msrcrypto + - react-native-securerandom + - supports-color + dev: true + /@sphereon/ssi-types@0.17.6-unstable.69: resolution: {integrity: sha512-VwjVd6XhoV5QecWcRh0RpBf5N324tfhYcQrVk9se3brqMNMauZTBbhUhk+QLdwFd+u/1+IfEWnS28HyIU7lXHQ==} dependencies: @@ -3456,6 +3555,10 @@ packages: jwt-decode: 3.1.2 dev: false + /@stablelib/aead@1.0.1: + resolution: {integrity: sha512-q39ik6sxGHewqtO0nP4BuSe3db5G1fEJE8ukvngS2gLkBXyy6E7pLubhbYgnkDFv6V8cWaxcE4Xn0t6LWcJkyg==} + dev: true + /@stablelib/binary@1.0.1: resolution: {integrity: sha512-ClJWvmL6UBM/wjkvv/7m5VP3GMr9t0osr4yVgLZsLCOz4hGN9gIAFEqnJ0TsSMAN+n840nf2cHZnA5/KFqHC7Q==} dependencies: @@ -3465,6 +3568,28 @@ packages: resolution: {integrity: sha512-Kre4Y4kdwuqL8BR2E9hV/R5sOrUj6NanZaZis0V6lX5yzqC3hBuVSDXUIBqQv/sCpmuWRiHLwqiT1pqqjuBXoQ==} dev: true + /@stablelib/chacha20poly1305@1.0.1: + resolution: {integrity: sha512-MmViqnqHd1ymwjOQfghRKw2R/jMIGT3wySN7cthjXCBdO+qErNPUBnRzqNpnvIwg7JBCg3LdeCZZO4de/yEhVA==} + dependencies: + '@stablelib/aead': 1.0.1 + '@stablelib/binary': 1.0.1 + '@stablelib/chacha': 1.0.1 + '@stablelib/constant-time': 1.0.1 + '@stablelib/poly1305': 1.0.1 + '@stablelib/wipe': 1.0.1 + dev: true + + /@stablelib/chacha@1.0.1: + resolution: {integrity: sha512-Pmlrswzr0pBzDofdFuVe1q7KdsHKhhU24e8gkEwnTGOmlC7PADzLVxGdn2PoNVBBabdg0l/IfLKg6sHAbTQugg==} + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/wipe': 1.0.1 + dev: true + + /@stablelib/constant-time@1.0.1: + resolution: {integrity: sha512-tNOs3uD0vSJcK6z1fvef4Y+buN7DXhzHDPqRLSXUel1UfqMB1PWNsnnAezrKfEwTLpN0cGH2p9NNjs6IqeD0eg==} + dev: true + /@stablelib/ed25519@1.0.3: resolution: {integrity: sha512-puIMWaX9QlRsbhxfDc5i+mNPMY+0TmQEskunY1rZEBPi1acBCVQAhnsk/1Hk50DGPtVsZtAWQg4NHGlVaO9Hqg==} dependencies: @@ -3484,6 +3609,13 @@ packages: '@stablelib/bytes': 1.0.1 dev: true + /@stablelib/poly1305@1.0.1: + resolution: {integrity: sha512-1HlG3oTSuQDOhSnLwJRKeTRSAdFNVB/1djy2ZbS35rBSJ/PFqx9cf9qatinWghC2UbfOYD8AcrtbUQl8WoxabA==} + dependencies: + '@stablelib/constant-time': 1.0.1 + '@stablelib/wipe': 1.0.1 + dev: true + /@stablelib/random@1.0.0: resolution: {integrity: sha512-G9vwwKrNCGMI/uHL6XeWe2Nk4BuxkYyWZagGaDU9wrsuV+9hUwNI1lok2WVo8uJDa2zx7ahNwN7Ij983hOUFEw==} dependencies: @@ -3497,6 +3629,14 @@ packages: '@stablelib/binary': 1.0.1 '@stablelib/wipe': 1.0.1 + /@stablelib/sha256@1.0.1: + resolution: {integrity: sha512-GIIH3e6KH+91FqGV42Kcj71Uefd/QEe7Dy42sBTeqppXV95ggCcxLTk39bEr+lZfJmp+ghsR07J++ORkRELsBQ==} + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/hash': 1.0.1 + '@stablelib/wipe': 1.0.1 + dev: true + /@stablelib/sha512@1.0.1: resolution: {integrity: sha512-13gl/iawHV9zvDKciLo1fQ8Bgn2Pvf7OV6amaRVKiq3pjQ3UmEpXxWiAfV8tYjUpeZroBxtyrwtdooQT/i3hzw==} dependencies: @@ -3515,6 +3655,24 @@ packages: '@stablelib/wipe': 1.0.1 dev: true + /@stablelib/xchacha20@1.0.1: + resolution: {integrity: sha512-1YkiZnFF4veUwBVhDnDYwo6EHeKzQK4FnLiO7ezCl/zu64uG0bCCAUROJaBkaLH+5BEsO3W7BTXTguMbSLlWSw==} + dependencies: + '@stablelib/binary': 1.0.1 + '@stablelib/chacha': 1.0.1 + '@stablelib/wipe': 1.0.1 + dev: true + + /@stablelib/xchacha20poly1305@1.0.1: + resolution: {integrity: sha512-B1Abj0sMJ8h3HNmGnJ7vHBrAvxuNka6cJJoZ1ILN7iuacXp7sUYcgOVEOTLWj+rtQMpspY9tXSCRLPmN1mQNWg==} + dependencies: + '@stablelib/aead': 1.0.1 + '@stablelib/chacha20poly1305': 1.0.1 + '@stablelib/constant-time': 1.0.1 + '@stablelib/wipe': 1.0.1 + '@stablelib/xchacha20': 1.0.1 + dev: true + /@tokenizer/token@0.3.0: resolution: {integrity: sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==} dev: true @@ -3692,6 +3850,14 @@ packages: '@transmute/ld-key-pair': 0.7.0-unstable.81 dev: true + /@trust/keyto@2.0.0-alpha1: + resolution: {integrity: sha512-VmlOa+nOaDzhEUfprnVp7RxFQyuEwA4fJ5+smnsud5WM01gU16yQnO/ejZnDVMGXuq/sUwTa5pCej4JhkKA5Sg==} + dependencies: + asn1.js: 5.4.1 + base64url: 3.0.1 + elliptic: 6.5.4 + dev: true + /@tsconfig/node10@1.0.9: resolution: {integrity: sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==} dev: true @@ -4097,6 +4263,19 @@ packages: graphql: 15.8.0 wonka: 4.0.15 + /@veramo/core@4.2.0: + resolution: {integrity: sha512-HIqbXfCbwOAJelR5Ohsm22vr63cy6ND8Ua/+9wfMDAiymUUS7NryaJ/v6NRtnmIrNZqUMDdR9/TWdp4cCq5eBg==} + dependencies: + credential-status: 2.0.6 + debug: 4.3.4 + did-jwt-vc: 3.2.15 + did-resolver: 4.1.0 + events: 3.3.0 + z-schema: 5.0.5 + transitivePeerDependencies: + - supports-color + dev: true + /@xmldom/xmldom@0.7.13: resolution: {integrity: sha512-lm2GW5PkosIzccsaZIz7tp8cPADSIlIHWDFTR1N0SzfinhhYgeIQjFMz4rYzanCScr3DqQLeomUDArp6MWKm+g==} engines: {node: '>=10.0.0'} @@ -4124,6 +4303,12 @@ packages: argparse: 2.0.1 dev: true + /@zxing/text-encoding@0.9.0: + resolution: {integrity: sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==} + requiresBuild: true + dev: true + optional: true + /JSONStream@1.3.5: resolution: {integrity: sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ==} hasBin: true @@ -4445,6 +4630,19 @@ packages: resolution: {integrity: sha512-usgMoyXjMbx/ZPdzTSXExhMPur2FTdz/Vo5PVx2gIaBcdAAJNOFlsdgqveM8Cff7W0v+xrf9BwjOV26JSAF9qA==} dev: false + /asmcrypto.js@2.3.2: + resolution: {integrity: sha512-3FgFARf7RupsZETQ1nHnhLUUvpcttcCq1iZCaVAbJZbCZ5VNRrNyvpDyHTOb0KC3llFcsyOT/a99NZcCbeiEsA==} + dev: true + + /asn1.js@5.4.1: + resolution: {integrity: sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==} + dependencies: + bn.js: 4.12.0 + inherits: 2.0.4 + minimalistic-assert: 1.0.1 + safer-buffer: 2.1.2 + dev: true + /asn1js@3.0.5: resolution: {integrity: sha512-FVnvrKJwpt9LP2lAMl8qZswRNm3T4q9CON+bxldk2iwk3FFpuwhx2FfinyitizWHsVYyaY+y5JzDR0rCMV5yTQ==} engines: {node: '>=12.0.0'} @@ -4510,13 +4708,11 @@ packages: resolution: {integrity: sha512-aHe97M7DXt+dkpa8fHlCcm1CnskAHrJqEfMI0KN7dwqlzml/aUe1AGt6lk51HzrSfVD67xOso84sOpr+0wIe2w==} dependencies: base-64: 0.1.0 - dev: false /b64u-lite@1.1.0: resolution: {integrity: sha512-929qWGDVCRph7gQVTC6koHqQIpF4vtVaSbwLltFQo44B1bYUquALswZdBKFfrJCPEnsCOvWkJsPdQYZ/Ukhw8A==} dependencies: b64-lite: 1.4.0 - dev: false /babel-core@7.0.0-bridge.0(@babel/core@7.23.0): resolution: {integrity: sha512-poPX9mZH/5CSanm50Q+1toVci6pv5KSRv/5TWCwtzQS5XEwn40BcCrgIeMFWP9CKKIniKXNxoIOnOq4VVlGXhg==} @@ -4699,7 +4895,6 @@ packages: /base-64@0.1.0: resolution: {integrity: sha512-Y5gU45svrR5tI2Vt/X9GPd3L0HNIKzGu202EjxrXMpuc2V2CiKgemAbUUsqYmZJvPtCXoUKjNZwBJzsNScUbXA==} - dev: false /base-x@3.0.9: resolution: {integrity: sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==} @@ -4731,6 +4926,10 @@ packages: safe-buffer: 5.1.2 dev: false + /bech32@2.0.0: + resolution: {integrity: sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg==} + dev: true + /before-after-hook@2.2.3: resolution: {integrity: sha512-NzUnlZexiaH/46WDhANlyR2bXRopNg4F/zuSA3OpZnllCUgRaOF2znDioDWrmbNVsuZk6l9pMquQB38cfBZwkQ==} dev: true @@ -5040,6 +5239,10 @@ packages: /canonicalize@1.0.8: resolution: {integrity: sha512-0CNTVCLZggSh7bc5VkX5WWPWO+cyZbNd07IHIsSXLia/eAq+r836hgk+8BKoEh7949Mda87VUOitx5OddVj64A==} + /canonicalize@2.0.0: + resolution: {integrity: sha512-ulDEYPv7asdKvqahuAY35c1selLdzDwHqugK92hfkzvlDCwXRRelDkR+Er33md/PtnpqHemgkuDPanZ4fiYZ8w==} + dev: true + /canvas@2.11.2: resolution: {integrity: sha512-ItanGBMrmRV7Py2Z+Xhs7cT+FNt5K0vPL4p9EZ/UX/Mu7hFbkxSjKF2KVtPwX7UYWp7dRKnrTvReflgrItJbdw==} engines: {node: '>=6'} @@ -5109,6 +5312,13 @@ packages: resolution: {integrity: sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==} engines: {node: '>=8'} + /cipher-base@1.0.4: + resolution: {integrity: sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==} + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + /cjs-module-lexer@1.2.3: resolution: {integrity: sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==} dev: true @@ -5570,6 +5780,16 @@ packages: typescript: 5.0.4 dev: true + /create-hash@1.2.0: + resolution: {integrity: sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==} + dependencies: + cipher-base: 1.0.4 + inherits: 2.0.4 + md5.js: 1.3.5 + ripemd160: 2.0.2 + sha.js: 2.4.11 + dev: true + /create-jest@29.7.0(@types/node@18.18.0)(ts-node@10.9.1): resolution: {integrity: sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} @@ -5593,6 +5813,13 @@ packages: resolution: {integrity: sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==} dev: true + /credential-status@2.0.6: + resolution: {integrity: sha512-l5ZwSbX/UXFJ3DQ3dFt4rc2BtfUu/rhlkefR7BL9EZsKPyCe21okJA9mDy4h/nXvMEwpYjSQEa5vzR7KZqhI9g==} + dependencies: + did-jwt: 6.11.6 + did-resolver: 4.1.0 + dev: true + /credentials-context@2.0.0: resolution: {integrity: sha512-/mFKax6FK26KjgV2KW2D4YqKgoJ5DVJpNt87X2Jc9IxT2HBMy7nEIlc+n7pEi+YFFe721XqrvZPd+jbyyBjsvQ==} dev: false @@ -5881,6 +6108,45 @@ packages: resolution: {integrity: sha512-iFpszgSxc7d1kNBJWC+PAzNTpe5LPalzsIunTMIpbG3O37Q7Zi7u4iIaedaM7UhziBhT+Agr9DyvAiXSUyfepQ==} dev: false + /did-jwt-vc@3.2.15: + resolution: {integrity: sha512-M/WPiL34CQUiN4bvWnZ0OFHJ3usPtstfQfbNbHAWHvwjeCGi7nAdv62VXHgy2xIhJMc790hH7PsMN3i6SCGEyg==} + engines: {node: '>=18'} + dependencies: + did-jwt: 7.4.7 + did-resolver: 4.1.0 + dev: true + + /did-jwt@6.11.6: + resolution: {integrity: sha512-OfbWknRxJuUqH6Lk0x+H1FsuelGugLbBDEwsoJnicFOntIG/A4y19fn0a8RLxaQbWQ5gXg0yDq5E2huSBiiXzw==} + dependencies: + '@stablelib/ed25519': 1.0.3 + '@stablelib/random': 1.0.2 + '@stablelib/sha256': 1.0.1 + '@stablelib/x25519': 1.0.3 + '@stablelib/xchacha20poly1305': 1.0.1 + bech32: 2.0.0 + canonicalize: 2.0.0 + did-resolver: 4.1.0 + elliptic: 6.5.4 + js-sha3: 0.8.0 + multiformats: 9.9.0 + uint8arrays: 3.1.1 + dev: true + + /did-jwt@7.4.7: + resolution: {integrity: sha512-Apz7nIfIHSKWIMaEP5L/K8xkwByvjezjTG0xiqwKdnNj1x8M0+Yasury5Dm/KPltxi2PlGfRPf3IejRKZrT8mQ==} + dependencies: + '@noble/ciphers': 0.4.1 + '@noble/curves': 1.3.0 + '@noble/hashes': 1.3.3 + '@scure/base': 1.1.5 + canonicalize: 2.0.0 + did-resolver: 4.1.0 + multibase: 4.0.6 + multiformats: 9.9.0 + uint8arrays: 3.1.1 + dev: true + /did-resolver@4.1.0: resolution: {integrity: sha512-S6fWHvCXkZg2IhS4RcVHxwuyVejPR7c+a4Go0xbQ9ps5kILa8viiYQgrM4gfTyeTjJ0ekgJH9gk/BawTpmkbZA==} dev: true @@ -6380,6 +6646,11 @@ packages: resolution: {integrity: sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw==} dev: true + /events@3.3.0: + resolution: {integrity: sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==} + engines: {node: '>=0.8.x'} + dev: true + /exec-async@2.2.0: resolution: {integrity: sha512-87OpwcEiMia/DeiKFzaQNBNFeN3XkkpYIh9FyOqq5mS2oKv3CBE67PXoEKcr6nodWdXNogTiQ0jE2NGuoffXPw==} @@ -6499,6 +6770,15 @@ packages: transitivePeerDependencies: - supports-color + /expo-crypto@12.6.0(expo@48.0.20): + resolution: {integrity: sha512-wSq64eIJxk4lQBidtcW9wNF5AgO/UvV8W8mDhb7bo6P3xH41yvu/P4FcxevQY1taGA8VHD+fO+xQDrhPiHzFqQ==} + peerDependencies: + expo: '*' + dependencies: + base64-js: 1.5.1 + expo: 48.0.20(@babel/core@7.23.0) + dev: true + /expo-file-system@15.2.2(expo@48.0.20): resolution: {integrity: sha512-LFkOLcWwlmnjkURxZ3/0ukS35OswX8iuQknLHRHeyk8mUA8fpRPPelD/a1lS+yclqfqavMJmTXVKM1Nsq5XVMA==} peerDependencies: @@ -7438,6 +7718,15 @@ packages: dependencies: function-bind: 1.1.1 + /hash-base@3.1.0: + resolution: {integrity: sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==} + engines: {node: '>=4'} + dependencies: + inherits: 2.0.4 + readable-stream: 3.6.2 + safe-buffer: 5.2.1 + dev: true + /hash.js@1.1.7: resolution: {integrity: sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==} dependencies: @@ -7782,6 +8071,14 @@ packages: resolution: {integrity: sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==} engines: {node: '>= 0.10'} + /is-arguments@1.1.1: + resolution: {integrity: sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==} + engines: {node: '>= 0.4'} + dependencies: + call-bind: 1.0.2 + has-tostringtag: 1.0.0 + dev: true + /is-array-buffer@3.0.2: resolution: {integrity: sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==} dependencies: @@ -7870,6 +8167,13 @@ packages: engines: {node: '>=6'} dev: true + /is-generator-function@1.0.10: + resolution: {integrity: sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==} + engines: {node: '>= 0.4'} + dependencies: + has-tostringtag: 1.0.0 + dev: true + /is-glob@2.0.1: resolution: {integrity: sha512-a1dBeB19NXsf/E0+FHqkagizel/LQw2DjSQpvQrj3zT+jYPpaUCryPnrQajXKFLCMuf4I6FhRpaGtw4lPrG6Eg==} engines: {node: '>=0.10.0'} @@ -8642,6 +8946,10 @@ packages: resolution: {integrity: sha512-xezGJmOb4lk/M1ZZLTR/jaBHQ4gG/lqQnJqdIv4721DMggsa1bDVlHXNeHYogaIEHD9vCRv0fcL4hMA+Coarkg==} dev: false + /js-sha3@0.8.0: + resolution: {integrity: sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==} + dev: true + /js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} @@ -9079,10 +9387,22 @@ packages: /lodash.debounce@4.0.8: resolution: {integrity: sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow==} + /lodash.get@4.4.2: + resolution: {integrity: sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==} + dev: true + + /lodash.isequal@4.5.0: + resolution: {integrity: sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==} + dev: true + /lodash.ismatch@4.4.0: resolution: {integrity: sha512-fPMfXjGQEV9Xsq/8MTSgUf255gawYRbjwMyDbcvDhXgV7enSZA0hynz6vMPnpAb5iONEzBHBPsT+0zes5Z301g==} dev: true + /lodash.isplainobject@4.0.6: + resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} + dev: true + /lodash.memoize@4.1.2: resolution: {integrity: sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==} dev: true @@ -9246,6 +9566,14 @@ packages: dependencies: buffer-alloc: 1.2.0 + /md5.js@1.3.5: + resolution: {integrity: sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==} + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + /md5@2.2.1: resolution: {integrity: sha512-PlGG4z5mBANDGCKsYQe0CaUYHdZYZt8ZPZLmEt+Urf0W4GlpTX4HescwHU+dc9+Z/G/vZKYZYFrwgm9VxK6QOQ==} dependencies: @@ -9891,7 +10219,14 @@ packages: /msrcrypto@1.5.8: resolution: {integrity: sha512-ujZ0TRuozHKKm6eGbKHfXef7f+esIhEckmThVnz7RNyiOJd7a6MXj2JGBoL9cnPDW+JMG16MoTUh5X+XXjI66Q==} - dev: false + + /multibase@4.0.6: + resolution: {integrity: sha512-x23pDe5+svdLz/k5JPGCVdfn7Q5mZVMBETiC+ORfO+sor9Sgs0smJzAjfTbM5tckeCqnaUuMYoz+k3RXMmJClQ==} + engines: {node: '>=12.0.0', npm: '>=6.0.0'} + deprecated: This module has been superseded by the multiformats module + dependencies: + '@multiformats/base-x': 4.0.1 + dev: true /multiformats@9.9.0: resolution: {integrity: sha512-HoMUjhH9T8DDBNT+6xzkrd9ga/XiBI4xLr58LJACwK6G3HTOPeMz4nB4KJs33L2BelrIJa7P0VuNaVF3hMYfjg==} @@ -11094,6 +11429,19 @@ packages: engines: {node: '>= 0.8'} dev: false + /randombytes@2.1.0: + resolution: {integrity: sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==} + dependencies: + safe-buffer: 5.2.1 + dev: true + + /randomfill@1.0.4: + resolution: {integrity: sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==} + dependencies: + randombytes: 2.1.0 + safe-buffer: 5.2.1 + dev: true + /range-parser@1.2.1: resolution: {integrity: sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==} engines: {node: '>= 0.6'} @@ -11183,7 +11531,6 @@ packages: dependencies: base64-js: 1.5.1 react-native: 0.71.13(@babel/core@7.23.0)(@babel/preset-env@7.22.20)(react@18.2.0) - dev: false /react-native@0.71.13(@babel/core@7.23.0)(@babel/preset-env@7.22.20)(react@18.2.0): resolution: {integrity: sha512-zEa69YQNLdv8Sf5Pn0CNDB1K9eGuNy1KoMNxXlrZ89JZ8d02b5hihZIoOCCIwhH+iPgslYwr3ZoGd3AY6FMrgw==} @@ -11590,6 +11937,13 @@ packages: glob: 10.3.10 dev: true + /ripemd160@2.0.2: + resolution: {integrity: sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==} + dependencies: + hash-base: 3.1.0 + inherits: 2.0.4 + dev: true + /roarr@7.15.1: resolution: {integrity: sha512-0ExL9rjOXeQPvQvQo8IcV8SR2GTXmDr1FQFlY2HiAV+gdVQjaVZNOx9d4FI2RqFFsd0sNsiw2TRS/8RU9g0ZfA==} engines: {node: '>=12.0'} @@ -11787,6 +12141,14 @@ packages: /setprototypeof@1.2.0: resolution: {integrity: sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==} + /sha.js@2.4.11: + resolution: {integrity: sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==} + hasBin: true + dependencies: + inherits: 2.0.4 + safe-buffer: 5.2.1 + dev: true + /shallow-clone@3.0.1: resolution: {integrity: sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==} engines: {node: '>=8'} @@ -12031,7 +12393,6 @@ packages: /str2buf@1.3.0: resolution: {integrity: sha512-xIBmHIUHYZDP4HyoXGHYNVmxlXLXDrtFHYT0eV6IOdEj3VO9ccaF1Ejl9Oq8iFjITllpT8FhaXb4KsNmw+3EuA==} - dev: false /stream-buffers@2.2.0: resolution: {integrity: sha512-uyQK/mx5QjHun80FLJTfaWE7JtwfRMKBLkMne6udYOmvH0CawotVa7TfgYHzAnpphn4+TweIx1QKMnRIbipmUg==} @@ -13007,6 +13368,16 @@ packages: /util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} + /util@0.12.5: + resolution: {integrity: sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==} + dependencies: + inherits: 2.0.4 + is-arguments: 1.1.1 + is-generator-function: 1.0.10 + is-typed-array: 1.1.12 + which-typed-array: 1.1.11 + dev: true + /utils-merge@1.0.1: resolution: {integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==} engines: {node: '>= 0.4.0'} @@ -13067,6 +13438,15 @@ packages: builtins: 5.0.1 dev: true + /validator@13.11.0: + resolution: {integrity: sha512-Ii+sehpSfZy+At5nPdnyMhx78fEoPDkR2XW/zimHEL3MyGJQOCQ7WeP20jPYRz7ZCpcKLB21NxuXHF3bxjStBQ==} + engines: {node: '>= 0.10'} + dev: true + + /varint@6.0.0: + resolution: {integrity: sha512-cXEIW6cfr15lFv563k4GuVuW/fiwjknytD37jIOLSdSWuOI6WnO/oKwmP2FQTU2l01LP8/M5TSAJpzUaGe3uWg==} + dev: true + /vary@1.1.2: resolution: {integrity: sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==} engines: {node: '>= 0.8'} @@ -13084,6 +13464,14 @@ packages: dependencies: defaults: 1.0.4 + /web-encoding@1.1.5: + resolution: {integrity: sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==} + dependencies: + util: 0.12.5 + optionalDependencies: + '@zxing/text-encoding': 0.9.0 + dev: true + /webcrypto-core@1.7.7: resolution: {integrity: sha512-7FjigXNsBfopEj+5DV2nhNpfic2vumtjjgPmeDKk45z+MJwXKKfhPB7118Pfzrmh4jqOMST6Ch37iPAHoImg5g==} dependencies: @@ -13095,7 +13483,6 @@ packages: /webcrypto-shim@0.1.7: resolution: {integrity: sha512-JAvAQR5mRNRxZW2jKigWMjCMkjSdmP5cColRP1U/pTg69VgHXEi1orv5vVpJ55Zc5MIaPc1aaurzd9pjv2bveg==} - dev: false /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} @@ -13419,3 +13806,15 @@ packages: resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} engines: {node: '>=12.20'} dev: true + + /z-schema@5.0.5: + resolution: {integrity: sha512-D7eujBWkLa3p2sIpJA0d1pr7es+a7m0vFAnZLlCEKq/Ij2k0MLi9Br2UPxoxdYystm5K1yeBGzub0FlYUEWj2Q==} + engines: {node: '>=8.0.0'} + hasBin: true + dependencies: + lodash.get: 4.4.2 + lodash.isequal: 4.5.0 + validator: 13.11.0 + optionalDependencies: + commander: 9.5.0 + dev: true