diff --git a/credo.patch b/credo.patch new file mode 100644 index 0000000000..bf2c3980c4 --- /dev/null +++ b/credo.patch @@ -0,0 +1,100 @@ +diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts +index 99461b99..8789d22e 100644 +--- a/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts ++++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts +@@ -160,13 +160,24 @@ export class OpenId4VcHolderApi { + * @param options.tokenResponse Obtained through @see requestAccessToken + */ + public async requestCredentials(options: OpenId4VciRequestCredentialOptions) { +- const { resolvedCredentialOffer, cNonce, accessToken, ...credentialRequestOptions } = options ++ const { ++ resolvedCredentialOffer, ++ cNonce, ++ accessToken, ++ additionalProofOfPossessionPayloadClaims, ++ additionalCredentialRequestPayloadClaims, ++ customFormat, ++ ...credentialRequestOptions ++ } = options + + return this.openId4VciHolderService.acceptCredentialOffer(this.agentContext, { + resolvedCredentialOffer, + acceptCredentialOfferOptions: credentialRequestOptions, ++ customFormat, + accessToken, + cNonce, ++ additionalProofOfPossessionPayloadClaims, ++ additionalCredentialRequestPayloadClaims, + }) + } + +diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts +index 748a2847..7161cb61 100644 +--- a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts ++++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts +@@ -312,9 +312,18 @@ export class OpenId4VciHolderService { + accessToken?: string + cNonce?: string + clientId?: string ++ additionalProofOfPossessionPayloadClaims?: Record ++ additionalCredentialRequestPayloadClaims?: Record ++ customFormat?: string + } + ) { +- const { resolvedCredentialOffer, acceptCredentialOfferOptions } = options ++ const { ++ resolvedCredentialOffer, ++ acceptCredentialOfferOptions, ++ additionalProofOfPossessionPayloadClaims, ++ customFormat, ++ additionalCredentialRequestPayloadClaims, ++ } = options + const { metadata, version, offeredCredentials } = resolvedCredentialOffer + + const { credentialsToRequest, credentialBindingResolver, verifyCredentialStatus } = acceptCredentialOfferOptions +@@ -394,6 +403,11 @@ export class OpenId4VciHolderService { + .withEndpointMetadata(metadata) + .withAlg(signatureAlgorithm) + ++ if (additionalProofOfPossessionPayloadClaims) { ++ // @ts-expect-error: header can be empty is it won't be used and we only care about the payload claims ++ proofOfPossessionBuilder.withJwt({ header: {}, payload: additionalProofOfPossessionPayloadClaims }) ++ } ++ + // TODO: what if auth flow using did, and the did is different from client id. We now use the client_id + if (credentialBinding.method === 'did') { + proofOfPossessionBuilder.withClientId(parseDid(credentialBinding.didUrl).did).withKid(credentialBinding.didUrl) +@@ -424,11 +438,17 @@ export class OpenId4VciHolderService { + .withCredentialEndpoint(metadata.credential_endpoint) + .withToken(tokenResponse.access_token) + ++ if (customFormat) { ++ credentialRequestBuilder.withFormat(customFormat) ++ } ++ + const credentialRequestClient = credentialRequestBuilder.build() + const credentialResponse = await credentialRequestClient.acquireCredentialsUsingProof({ + proofInput: proofOfPossession, + credentialTypes: getTypesFromCredentialSupported(offeredCredentialConfiguration), +- format: offeredCredentialConfiguration.format, ++ format: customFormat ? undefined : offeredCredentialConfiguration.format, ++ // @ts-expect-error: custom patched file ++ additionalRequestClaims: additionalCredentialRequestPayloadClaims, + }) + + newCNonce = credentialResponse.successBody?.c_nonce +diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts +index 8b15fe97..9af2ac99 100644 +--- a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts ++++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts +@@ -117,6 +117,10 @@ export interface OpenId4VciCredentialRequestOptions extends Omit ++ additionalCredentialRequestPayloadClaims?: Record ++ customFormat?: string + } + + /** diff --git a/packages/core/src/modules/x509/__tests__/X509Service.test.ts b/packages/core/src/modules/x509/__tests__/X509Service.test.ts index 49e0dad553..ccb04c886c 100644 --- a/packages/core/src/modules/x509/__tests__/X509Service.test.ts +++ b/packages/core/src/modules/x509/__tests__/X509Service.test.ts @@ -173,7 +173,24 @@ describe('X509Service', () => { }) }) - it('should generate a self-signed X509 Certificate', async () => { + it('should generate a self-signed X509 Certificate with Ed25519', async () => { + const key = await agentContext.wallet.createKey({ keyType: KeyType.Ed25519 }) + + const selfSignedCertificate = await X509Service.createSelfSignedCertificate(agentContext, { + key, + }) + + expect(selfSignedCertificate.publicKey.publicKeyBase58).toStrictEqual(key.publicKeyBase58) + expect(selfSignedCertificate.sanDnsNames).toStrictEqual([]) + expect(selfSignedCertificate.sanUriNames).toStrictEqual([]) + + const pemCertificate = selfSignedCertificate.toString('pem') + + expect(pemCertificate.startsWith('-----BEGIN CERTIFICATE-----\n')).toBeTruthy() + expect(pemCertificate.endsWith('\n-----END CERTIFICATE-----')).toBeTruthy() + }) + + it('should generate a self-signed X509 Certificate with P256', async () => { const key = await agentContext.wallet.createKey({ keyType: KeyType.P256 }) const selfSignedCertificate = await X509Service.createSelfSignedCertificate(agentContext, { diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts index 99461b9913..8789d22e1b 100644 --- a/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VcHolderApi.ts @@ -160,13 +160,24 @@ export class OpenId4VcHolderApi { * @param options.tokenResponse Obtained through @see requestAccessToken */ public async requestCredentials(options: OpenId4VciRequestCredentialOptions) { - const { resolvedCredentialOffer, cNonce, accessToken, ...credentialRequestOptions } = options + const { + resolvedCredentialOffer, + cNonce, + accessToken, + additionalProofOfPossessionPayloadClaims, + additionalCredentialRequestPayloadClaims, + customFormat, + ...credentialRequestOptions + } = options return this.openId4VciHolderService.acceptCredentialOffer(this.agentContext, { resolvedCredentialOffer, acceptCredentialOfferOptions: credentialRequestOptions, + customFormat, accessToken, cNonce, + additionalProofOfPossessionPayloadClaims, + additionalCredentialRequestPayloadClaims, }) } diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts index 748a284746..7161cb61ce 100644 --- a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderService.ts @@ -312,9 +312,18 @@ export class OpenId4VciHolderService { accessToken?: string cNonce?: string clientId?: string + additionalProofOfPossessionPayloadClaims?: Record + additionalCredentialRequestPayloadClaims?: Record + customFormat?: string } ) { - const { resolvedCredentialOffer, acceptCredentialOfferOptions } = options + const { + resolvedCredentialOffer, + acceptCredentialOfferOptions, + additionalProofOfPossessionPayloadClaims, + customFormat, + additionalCredentialRequestPayloadClaims, + } = options const { metadata, version, offeredCredentials } = resolvedCredentialOffer const { credentialsToRequest, credentialBindingResolver, verifyCredentialStatus } = acceptCredentialOfferOptions @@ -394,6 +403,11 @@ export class OpenId4VciHolderService { .withEndpointMetadata(metadata) .withAlg(signatureAlgorithm) + if (additionalProofOfPossessionPayloadClaims) { + // @ts-expect-error: header can be empty is it won't be used and we only care about the payload claims + proofOfPossessionBuilder.withJwt({ header: {}, payload: additionalProofOfPossessionPayloadClaims }) + } + // TODO: what if auth flow using did, and the did is different from client id. We now use the client_id if (credentialBinding.method === 'did') { proofOfPossessionBuilder.withClientId(parseDid(credentialBinding.didUrl).did).withKid(credentialBinding.didUrl) @@ -424,11 +438,17 @@ export class OpenId4VciHolderService { .withCredentialEndpoint(metadata.credential_endpoint) .withToken(tokenResponse.access_token) + if (customFormat) { + credentialRequestBuilder.withFormat(customFormat) + } + const credentialRequestClient = credentialRequestBuilder.build() const credentialResponse = await credentialRequestClient.acquireCredentialsUsingProof({ proofInput: proofOfPossession, credentialTypes: getTypesFromCredentialSupported(offeredCredentialConfiguration), - format: offeredCredentialConfiguration.format, + format: customFormat ? undefined : offeredCredentialConfiguration.format, + // @ts-expect-error: custom patched file + additionalRequestClaims: additionalCredentialRequestPayloadClaims, }) newCNonce = credentialResponse.successBody?.c_nonce diff --git a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts index 8b15fe97e5..9af2ac99f2 100644 --- a/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts +++ b/packages/openid4vc/src/openid4vc-holder/OpenId4VciHolderServiceOptions.ts @@ -117,6 +117,10 @@ export interface OpenId4VciCredentialRequestOptions extends Omit + additionalCredentialRequestPayloadClaims?: Record + customFormat?: string } /** diff --git a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts index bdc182cc04..73262c045a 100644 --- a/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts +++ b/packages/openid4vc/src/openid4vc-verifier/OpenId4VcSiopVerifierService.ts @@ -153,6 +153,11 @@ export class OpenId4VcSiopVerifierService { state, requestByReferenceURI: hostedAuthorizationRequestUri, jwtIssuer, + responseURI: joinUriParts(this.config.baseUrl, [ + options.verifier.verifierId, + this.config.authorizationEndpoint.endpointPath, + ]), + responseURIType: 'response_uri', }) // NOTE: it's not possible to set the uri scheme when using the RP to create an auth request, only lower level