From 0e3c7e9570dc0bb0ce409d2669e9559b0f8fee0e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linas=20Is=CC=8Cganaitis?= Date: Fri, 6 Oct 2023 12:43:06 +0200 Subject: [PATCH 1/4] Enable authorization code flow helper methods. Fix building authorization URI methods. --- packages/client/lib/AccessTokenClient.ts | 36 +++++++------- packages/client/lib/OpenID4VCIClient.ts | 57 ++++++++++++--------- packages/common/lib/functions/Encoding.ts | 60 +++++++++++------------ 3 files changed, 80 insertions(+), 73 deletions(-) diff --git a/packages/client/lib/AccessTokenClient.ts b/packages/client/lib/AccessTokenClient.ts index 5b669e39..bab1788f 100644 --- a/packages/client/lib/AccessTokenClient.ts +++ b/packages/client/lib/AccessTokenClient.ts @@ -4,10 +4,10 @@ import { AccessTokenResponse, assertedUniformCredentialOffer, AuthorizationServerOpts, + AuthzFlowType, EndpointMetadata, getIssuerFromCredentialOfferPayload, GrantTypes, - isPreAuthCode, IssuerOpts, OpenIDResponse, PRE_AUTH_CODE_LITERAL, @@ -67,6 +67,7 @@ export class AccessTokenClient { issuerOpts?: IssuerOpts; }): Promise> { this.validate(accessTokenRequest, isPinRequired); + const requestTokenURL = AccessTokenClient.determineTokenURL({ asOpts, issuerOpts, @@ -76,6 +77,7 @@ export class AccessTokenClient { ? await MetadataClient.retrieveAllMetadata(issuerOpts.issuer, { errorOnNotFound: false }) : undefined, }); + return this.sendAuthCode(requestTokenURL, accessTokenRequest); } @@ -83,38 +85,36 @@ export class AccessTokenClient { const { asOpts, pin, codeVerifier, code, redirectUri } = opts; const credentialOfferRequest = await toUniformCredentialOfferRequest(opts.credentialOffer); const request: Partial = {}; + if (asOpts?.clientId) { request.client_id = asOpts.clientId; } - this.assertNumericPin(this.isPinRequiredValue(credentialOfferRequest.credential_offer), pin); - request.user_pin = pin; + if (credentialOfferRequest.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) { + this.assertNumericPin(this.isPinRequiredValue(credentialOfferRequest.credential_offer), pin); + request.user_pin = pin; - const isPreAuth = isPreAuthCode(credentialOfferRequest); - if (isPreAuth) { - if (codeVerifier) { - throw new Error('Cannot pass a code_verifier when flow type is pre-authorized'); - } request.grant_type = GrantTypes.PRE_AUTHORIZED_CODE; // we actually know it is there because of the isPreAuthCode call request[PRE_AUTH_CODE_LITERAL] = credentialOfferRequest?.credential_offer.grants?.['urn:ietf:params:oauth:grant-type:pre-authorized_code']?.[PRE_AUTH_CODE_LITERAL]; + + return request as AccessTokenRequest; } - if (!isPreAuth && credentialOfferRequest.credential_offer.grants?.authorization_code?.issuer_state) { - this.throwNotSupportedFlow(); // not supported yet + + if (credentialOfferRequest.supportedFlows.includes(AuthzFlowType.AUTHORIZATION_CODE_FLOW)) { request.grant_type = GrantTypes.AUTHORIZATION_CODE; - } - if (codeVerifier) { - request.code_verifier = codeVerifier; request.code = code; request.redirect_uri = redirectUri; - request.grant_type = GrantTypes.AUTHORIZATION_CODE; - } - if (request.grant_type === GrantTypes.AUTHORIZATION_CODE && isPreAuth) { - throw Error('A pre_authorized_code flow cannot have an issuer state in the credential offer'); + + if (codeVerifier) { + request.code_verifier = codeVerifier; + } + + return request as AccessTokenRequest; } - return request as AccessTokenRequest; + throw new Error('Credential offer request does not follow neither pre-authorized code nor authorization code flow requirements.'); } private assertPreAuthorizedGrantType(grantType: GrantTypes): void { diff --git a/packages/client/lib/OpenID4VCIClient.ts b/packages/client/lib/OpenID4VCIClient.ts index 592d2dfd..32d85172 100644 --- a/packages/client/lib/OpenID4VCIClient.ts +++ b/packages/client/lib/OpenID4VCIClient.ts @@ -11,7 +11,6 @@ import { EndpointMetadataResult, OID4VCICredentialFormat, OpenId4VCIVersion, - OpenIDResponse, ProofOfPossessionCallbacks, PushedAuthorizationResponse, ResponseType, @@ -63,9 +62,6 @@ export class OpenID4VCIClient { alg?: Alg | string, clientId?: string, ) { - if (!credentialOffer.supportedFlows.includes(flowType)) { - throw Error(`Flows ${flowType} is not supported by issuer ${credentialOffer.credential_offer_uri}`); - } this._flowType = flowType; this._credentialOffer = credentialOffer; this._kid = kid; @@ -146,11 +142,12 @@ export class OpenID4VCIClient { authorization_details: JSON.stringify(this.handleAuthorizationDetails(authorizationDetails)), redirect_uri: redirectUri, scope: scope, + issuer_state: this.credentialOffer.issuerState, } as AuthorizationRequestV1_0_09; return convertJsonToURI(queryObj, { baseUrl: this._endpointMetadata.authorization_endpoint, - uriTypeProperties: ['redirect_uri', 'scope', 'authorization_details'], + uriTypeProperties: ['redirect_uri', 'scope', 'authorization_details', 'issuer_state'], version: this.version(), }); } @@ -162,7 +159,7 @@ export class OpenID4VCIClient { authorizationDetails, redirectUri, scope, - }: AuthRequestOpts): Promise> { + }: AuthRequestOpts): Promise { // 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) { @@ -187,17 +184,27 @@ export class OpenID4VCIClient { scope = `openid ${scope}`; } - //fixme: handle this for v11 - const queryObj: AuthorizationRequestV1_0_09 = { + const queryObj = { response_type: ResponseType.AUTH_CODE, client_id: clientId, code_challenge_method: codeChallengeMethod, code_challenge: codeChallenge, authorization_details: JSON.stringify(this.handleAuthorizationDetails(authorizationDetails)), redirect_uri: redirectUri, - scope: scope, + scope: scope || 'openid', + issuer_state: this.credentialOffer.issuerState || '', }; - return await formPost(parEndpoint, JSON.stringify(queryObj)); + + const response = await formPost(parEndpoint, new URLSearchParams(queryObj)); + + return convertJsonToURI( + { request_uri: response.successBody?.request_uri }, + { + baseUrl: this._endpointMetadata.authorization_endpoint, + uriTypeProperties: ['request_uri'], + version: this.version(), + }, + ); } public handleAuthorizationDetails(authorizationDetails?: AuthDetails | AuthDetails[]): AuthDetails | AuthDetails[] | undefined { @@ -237,7 +244,9 @@ export class OpenID4VCIClient { redirectUri?: string; }): Promise { const { pin, clientId, codeVerifier, code, redirectUri } = opts ?? {}; + this.assertIssuerData(); + if (clientId) { this._clientId = clientId; } @@ -300,24 +309,26 @@ export class OpenID4VCIClient { credentialOffer: this.credentialOffer, metadata: this.endpointMetadata, }); + requestBuilder.withTokenFromResponse(this.accessTokenResponse); if (this.endpointMetadata?.credentialIssuerMetadata) { const metadata = this.endpointMetadata.credentialIssuerMetadata; - const types = Array.isArray(credentialTypes) ? credentialTypes : [credentialTypes]; + const types = Array.isArray(credentialTypes) ? credentialTypes.sort() : [credentialTypes]; + if (metadata.credentials_supported && Array.isArray(metadata.credentials_supported)) { - for (const type of types) { - let typeSupported = false; - for (const credentialSupported of metadata.credentials_supported) { - if (!credentialSupported.types || credentialSupported.types.length === 0) { - throw Error('types is required in the credentials supported'); - } - if (credentialSupported.types.indexOf(type) != -1) { - typeSupported = true; - } + let typeSupported = false; + + metadata.credentials_supported.forEach((supportedCredential) => { + if (!supportedCredential.types || supportedCredential.types.length === 0) { + throw Error('types is required in the credentials supported'); } - if (!typeSupported) { - throw Error(`Not all credential types ${JSON.stringify(credentialTypes)} are supported by issuer ${this.getIssuer()}`); + if (supportedCredential.types.sort().every((t, i) => types[i] === t) || (types.length === 1 && types[0] === supportedCredential.id)) { + typeSupported = true; } + }); + + if (!typeSupported) { + 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; @@ -389,7 +400,7 @@ export class OpenID4VCIClient { result[0] = types; return result; } else { - return this.credentialOffer.credential_offer.credentials.map((c, index) => { + return this.credentialOffer.credential_offer.credentials.map((c) => { return typeof c === 'string' ? [c] : c.types; }); } diff --git a/packages/common/lib/functions/Encoding.ts b/packages/common/lib/functions/Encoding.ts index b84a918a..3e789765 100644 --- a/packages/common/lib/functions/Encoding.ts +++ b/packages/common/lib/functions/Encoding.ts @@ -1,4 +1,4 @@ -import { BAD_PARAMS, DecodeURIAsJsonOpts, EncodeJsonAsURIOpts, OpenId4VCIVersion, SearchValue } from '../types'; +import { BAD_PARAMS, DecodeURIAsJsonOpts, EncodeJsonAsURIOpts, SearchValue } from '../types'; /** * @function encodeJsonAsURI encodes a Json object into a URI @@ -29,39 +29,35 @@ export function convertJsonToURI( return encodeURIComponent(key.replace(' ', '')); } - let components: string; - if (opts?.version && opts.version > OpenId4VCIVersion.VER_1_0_08) { - // v11 changed from encoding every param to a encoded json object with a credential_offer param key - components = encodeAndStripWhitespace(JSON.stringify(json)); - } else { - for (const [key, value] of Object.entries(json)) { - if (!value) { - continue; - } - //Skip properties that are not of URL type - if (!opts?.uriTypeProperties?.includes(key)) { - results.push(`${key}=${value}`); - continue; - } - if (opts?.arrayTypeProperties?.includes(key) && Array.isArray(value)) { - results.push(value.map((v) => `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(v, /\./g)}`).join('&')); - continue; - } - const isBool = typeof value == 'boolean'; - const isNumber = typeof value == 'number'; - const isString = typeof value == 'string'; - let encoded; - if (isBool || isNumber) { - encoded = `${encodeAndStripWhitespace(key)}=${value}`; - } else if (isString) { - encoded = `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(value, /\./g)}`; - } else { - encoded = `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(JSON.stringify(value), /\./g)}`; - } - results.push(encoded); + for (const [key, value] of Object.entries(json)) { + if (!value) { + continue; + } + //Skip properties that are not of URL type + if (!opts?.uriTypeProperties?.includes(key)) { + results.push(`${key}=${value}`); + continue; + } + if (opts?.arrayTypeProperties?.includes(key) && Array.isArray(value)) { + results.push(value.map((v) => `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(v, /\./g)}`).join('&')); + continue; + } + const isBool = typeof value == 'boolean'; + const isNumber = typeof value == 'number'; + const isString = typeof value == 'string'; + let encoded; + if (isBool || isNumber) { + encoded = `${encodeAndStripWhitespace(key)}=${value}`; + } else if (isString) { + encoded = `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(value, /\./g)}`; + } else { + encoded = `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(JSON.stringify(value), /\./g)}`; } - components = results.join('&'); + results.push(encoded); } + + const components = results.join('&'); + if (opts?.baseUrl) { if (opts.baseUrl.endsWith('=')) { if (opts.param) { From 52f715f58edec7ba792db3484553b09f61912ac2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linas=20Is=CC=8Cganaitis?= Date: Fri, 6 Oct 2023 15:58:19 +0200 Subject: [PATCH 2/4] Remove flowType configuration, use clientId that was passed during client initialization, update tests --- packages/client/README.md | 1 - packages/client/lib/OpenID4VCIClient.ts | 70 +++++++------------ .../lib/__tests__/AccessTokenClient.spec.ts | 27 +------ packages/client/lib/__tests__/IT.spec.ts | 4 -- .../lib/__tests__/MattrE2E.spec.test.ts | 4 +- .../lib/__tests__/OpenID4VCIClient.spec.ts | 15 ++-- .../lib/__tests__/OpenID4VCIClientPAR.spec.ts | 22 +++--- packages/common/lib/functions/Encoding.ts | 60 ++++++++-------- .../lib/__tests__/ClientIssuerIT.spec.ts | 2 - .../issuer/lib/__tests__/VcIssuer.spec.ts | 3 +- 10 files changed, 75 insertions(+), 133 deletions(-) diff --git a/packages/client/README.md b/packages/client/README.md index ff28f488..b1da2a32 100644 --- a/packages/client/README.md +++ b/packages/client/README.md @@ -57,7 +57,6 @@ import { OpenID4VCIClient } from '@sphereon/oid4vci-client'; // The client is initiated from a URI. This URI is provided by the Issuer, typically as a URL or QR code. const client = await OpenID4VCIClient.fromURI({ uri: 'openid-initiate-issuance://?issuer=https%3A%2F%2Fissuer.research.identiproof.io&credential_type=OpenBadgeCredentialUrl&pre-authorized_code=4jLs9xZHEfqcoow0kHE7d1a8hUk6Sy-5bVSV2MqBUGUgiFFQi-ImL62T-FmLIo8hKA1UdMPH0lM1xAgcFkJfxIw9L-lI3mVs0hRT8YVwsEM1ma6N3wzuCdwtMU4bcwKp&user_pin_required=true', - flowType: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW, // The flow to use kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21#key-1', // Our DID. You can defer this also to when the acquireCredential method is called alg: Alg.ES256, // The signing Algorithm we will use. You can defer this also to when the acquireCredential method is called clientId: 'test-clientId', // The clientId if the Authrozation Service requires it. If a clientId is needed you can defer this also to when the acquireAccessToken method is called diff --git a/packages/client/lib/OpenID4VCIClient.ts b/packages/client/lib/OpenID4VCIClient.ts index 32d85172..fe90709a 100644 --- a/packages/client/lib/OpenID4VCIClient.ts +++ b/packages/client/lib/OpenID4VCIClient.ts @@ -1,7 +1,6 @@ import { AccessTokenResponse, Alg, - AuthorizationRequestV1_0_09, AuthzFlowType, CodeChallengeMethod, CredentialOfferPayloadV1_0_08, @@ -38,7 +37,6 @@ interface AuthDetails { } interface AuthRequestOpts { - clientId: string; codeChallenge: string; codeChallengeMethod: CodeChallengeMethod; authorizationDetails?: AuthDetails | AuthDetails[]; @@ -47,7 +45,6 @@ interface AuthRequestOpts { } export class OpenID4VCIClient { - private readonly _flowType: AuthzFlowType; private readonly _credentialOffer: CredentialOfferRequestWithBaseUrl; private _clientId?: string; private _kid: string | undefined; @@ -55,14 +52,7 @@ export class OpenID4VCIClient { private _endpointMetadata: EndpointMetadataResult | undefined; private _accessTokenResponse: AccessTokenResponse | undefined; - private constructor( - credentialOffer: CredentialOfferRequestWithBaseUrl, - flowType: AuthzFlowType, - kid?: string, - alg?: Alg | string, - clientId?: string, - ) { - this._flowType = flowType; + private constructor(credentialOffer: CredentialOfferRequestWithBaseUrl, kid?: string, alg?: Alg | string, clientId?: string) { this._credentialOffer = credentialOffer; this._kid = kid; this._alg = alg; @@ -71,7 +61,6 @@ export class OpenID4VCIClient { public static async fromURI({ uri, - flowType, kid, alg, retrieveServerMetadata, @@ -79,14 +68,13 @@ export class OpenID4VCIClient { resolveOfferUri, }: { uri: string; - flowType: AuthzFlowType; kid?: string; alg?: Alg | string; retrieveServerMetadata?: boolean; resolveOfferUri?: boolean; clientId?: string; }): Promise { - const client = new OpenID4VCIClient(await CredentialOfferClient.fromURI(uri, { resolve: resolveOfferUri }), flowType, kid, alg, clientId); + const client = new OpenID4VCIClient(await CredentialOfferClient.fromURI(uri, { resolve: resolveOfferUri }), kid, alg, clientId); if (retrieveServerMetadata === undefined || retrieveServerMetadata) { await client.retrieveServerMetadata(); @@ -102,14 +90,7 @@ export class OpenID4VCIClient { return this.endpointMetadata; } - public createAuthorizationRequestUrl({ - clientId, - codeChallengeMethod, - codeChallenge, - authorizationDetails, - redirectUri, - scope, - }: AuthRequestOpts): string { + 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) { @@ -129,31 +110,31 @@ export class OpenID4VCIClient { } // add 'openid' scope if not present - if (scope && !scope.includes('openid')) { - scope = `openid ${scope}`; + if (!scope?.includes('openid')) { + scope = ['openid', scope].filter((s) => !!s).join(' '); } - //fixme: handle this for v11 - const queryObj = { + const queryObj: { [key: string]: string } = { response_type: ResponseType.AUTH_CODE, - client_id: clientId, + client_id: this.clientId || '', code_challenge_method: codeChallengeMethod, code_challenge: codeChallenge, authorization_details: JSON.stringify(this.handleAuthorizationDetails(authorizationDetails)), redirect_uri: redirectUri, scope: scope, - issuer_state: this.credentialOffer.issuerState, - } as AuthorizationRequestV1_0_09; + }; + + if (this.credentialOffer.issuerState) { + queryObj['issuer_state'] = this.credentialOffer.issuerState; + } return convertJsonToURI(queryObj, { baseUrl: this._endpointMetadata.authorization_endpoint, uriTypeProperties: ['redirect_uri', 'scope', 'authorization_details', 'issuer_state'], - version: this.version(), }); } public async acquirePushedAuthorizationRequestURI({ - clientId, codeChallengeMethod, codeChallenge, authorizationDetails, @@ -180,29 +161,31 @@ export class OpenID4VCIClient { const parEndpoint: string = this._endpointMetadata.credentialIssuerMetadata.pushed_authorization_request_endpoint; // add 'openid' scope if not present - if (scope && !scope.includes('openid')) { - scope = `openid ${scope}`; + if (!scope?.includes('openid')) { + scope = ['openid', scope].filter((s) => !!s).join(' '); } - const queryObj = { + const queryObj: { [key: string]: string } = { response_type: ResponseType.AUTH_CODE, - client_id: clientId, + client_id: this.clientId || '', code_challenge_method: codeChallengeMethod, code_challenge: codeChallenge, authorization_details: JSON.stringify(this.handleAuthorizationDetails(authorizationDetails)), redirect_uri: redirectUri, - scope: scope || 'openid', - issuer_state: this.credentialOffer.issuerState || '', + scope: scope, }; + if (this.credentialOffer.issuerState) { + queryObj['issuer_state'] = this.credentialOffer.issuerState; + } + const response = await formPost(parEndpoint, new URLSearchParams(queryObj)); return convertJsonToURI( { request_uri: response.successBody?.request_uri }, { - baseUrl: this._endpointMetadata.authorization_endpoint, + baseUrl: this._endpointMetadata.credentialIssuerMetadata.authorization_endpoint, uriTypeProperties: ['request_uri'], - version: this.version(), }, ); } @@ -322,7 +305,10 @@ export class OpenID4VCIClient { if (!supportedCredential.types || supportedCredential.types.length === 0) { throw Error('types is required in the credentials supported'); } - if (supportedCredential.types.sort().every((t, i) => types[i] === t) || (types.length === 1 && types[0] === supportedCredential.id)) { + if ( + supportedCredential.types.sort().every((t, i) => types[i] === t) || + (types.length === 1 && (types[0] === supportedCredential.id || supportedCredential.types.includes(types[0]))) + ) { typeSupported = true; } }); @@ -406,10 +392,6 @@ export class OpenID4VCIClient { } } - get flowType(): AuthzFlowType { - return this._flowType; - } - issuerSupportedFlowTypes(): AuthzFlowType[] { return this.credentialOffer.supportedFlows; } diff --git a/packages/client/lib/__tests__/AccessTokenClient.spec.ts b/packages/client/lib/__tests__/AccessTokenClient.spec.ts index 52b7345a..045b2148 100644 --- a/packages/client/lib/__tests__/AccessTokenClient.spec.ts +++ b/packages/client/lib/__tests__/AccessTokenClient.spec.ts @@ -1,11 +1,4 @@ -import { - AccessTokenRequest, - AccessTokenRequestOpts, - AccessTokenResponse, - GrantTypes, - OpenIDResponse, - WellKnownEndpoints, -} from '@sphereon/oid4vci-common'; +import { AccessTokenRequest, AccessTokenResponse, GrantTypes, OpenIDResponse, WellKnownEndpoints } from '@sphereon/oid4vci-common'; import nock from 'nock'; import { AccessTokenClient } from '../AccessTokenClient'; @@ -204,24 +197,6 @@ describe('AccessTokenClient should', () => { ).rejects.toThrow(Error('Cannot set a pin, when the pin is not required.')); }); - it('get error if code_verifier is present when flow type is pre-authorized', async () => { - const accessTokenClient: AccessTokenClient = new AccessTokenClient(); - - nock(MOCK_URL).post(/.*/).reply(200, {}); - - const requestOpts: AccessTokenRequestOpts = { - credentialOffer: INITIATION_TEST, - pin: undefined, - codeVerifier: 'RylyWGQ-dzpObnEcoMBDIH9cTAwZXk1wYzktKxsOFgA', - code: 'LWCt225yj7gzT2cWeMP4hXj4B4oIYkEiGs4T6pfez91', - redirectUri: 'http://example.com/cb', - }; - - await expect(() => accessTokenClient.acquireAccessToken(requestOpts)).rejects.toThrow( - Error('Cannot pass a code_verifier when flow type is pre-authorized'), - ); - }); - it('get error if no as, issuer and metadata values are present', async () => { await expect(() => AccessTokenClient.determineTokenURL({ diff --git a/packages/client/lib/__tests__/IT.spec.ts b/packages/client/lib/__tests__/IT.spec.ts index 74c3a741..00ca84ea 100644 --- a/packages/client/lib/__tests__/IT.spec.ts +++ b/packages/client/lib/__tests__/IT.spec.ts @@ -1,7 +1,6 @@ import { AccessTokenResponse, Alg, - AuthzFlowType, CredentialOfferRequestWithBaseUrl, Jwt, OpenId4VCIVersion, @@ -72,7 +71,6 @@ describe('OID4VCI-Client should', () => { succeedWithAFullFlowWithClientSetup(); const client = await OpenID4VCIClient.fromURI({ uri: INITIATE_QR, - flowType: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', alg: Alg.ES256, clientId: 'test-clientId', @@ -84,7 +82,6 @@ describe('OID4VCI-Client should', () => { succeedWithAFullFlowWithClientSetup(); const client = await OpenID4VCIClient.fromURI({ uri: OFFER_QR, - flowType: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW, kid: 'did:example:ebfeb1f712ebc6f1c276e12ec21/keys/1', alg: Alg.ES256, clientId: 'test-clientId', @@ -93,7 +90,6 @@ describe('OID4VCI-Client should', () => { }); async function assertionOfsucceedWithAFullFlowWithClient(client: OpenID4VCIClient) { - expect(client.flowType).toEqual(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW); expect(client.credentialOffer).toBeDefined(); expect(client.endpointMetadata).toBeDefined(); expect(client.getIssuer()).toEqual('https://issuer.research.identiproof.io'); diff --git a/packages/client/lib/__tests__/MattrE2E.spec.test.ts b/packages/client/lib/__tests__/MattrE2E.spec.test.ts index 70f5f5f0..9000831c 100644 --- a/packages/client/lib/__tests__/MattrE2E.spec.test.ts +++ b/packages/client/lib/__tests__/MattrE2E.spec.test.ts @@ -1,4 +1,4 @@ -import { Alg, AuthzFlowType, Jwt } from '@sphereon/oid4vci-common'; +import { Alg, Jwt } from '@sphereon/oid4vci-common'; import { CredentialMapper } from '@sphereon/ssi-types'; import { fetch } from 'cross-fetch'; import { importJWK, JWK, SignJWT } from 'jose'; @@ -25,11 +25,9 @@ describe('OID4VCI-Client using Mattr issuer should', () => { const offer = await getCredentialOffer(format); const client = await OpenID4VCIClient.fromURI({ uri: offer.offerUrl, - flowType: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW, kid, alg: Alg.EdDSA, }); - expect(client.flowType).toEqual(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW); expect(client.credentialOffer).toBeDefined(); expect(client.endpointMetadata).toBeDefined(); expect(client.getCredentialEndpoint()).toEqual(`${ISSUER_URL}/oidc/v1/auth/credential`); diff --git a/packages/client/lib/__tests__/OpenID4VCIClient.spec.ts b/packages/client/lib/__tests__/OpenID4VCIClient.spec.ts index c3d9da59..4524f30e 100644 --- a/packages/client/lib/__tests__/OpenID4VCIClient.spec.ts +++ b/packages/client/lib/__tests__/OpenID4VCIClient.spec.ts @@ -1,4 +1,4 @@ -import { AuthzFlowType, CodeChallengeMethod, WellKnownEndpoints } from '@sphereon/oid4vci-common'; +import { CodeChallengeMethod, WellKnownEndpoints } from '@sphereon/oid4vci-common'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore import nock from 'nock'; @@ -15,8 +15,8 @@ describe('OpenID4VCIClient should', () => { nock(MOCK_URL).get(WellKnownEndpoints.OAUTH_AS).reply(404, {}); nock(MOCK_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(404, {}); client = await OpenID4VCIClient.fromURI({ + clientId: 'test-client', uri: 'openid-initiate-issuance://?issuer=https://server.example.com&credential_type=TestCredential', - flowType: AuthzFlowType.AUTHORIZATION_CODE_FLOW, }); }); @@ -29,7 +29,6 @@ describe('OpenID4VCIClient should', () => { // @ts-ignore client._endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`; const url = client.createAuthorizationRequestUrl({ - clientId: 'test-client', codeChallengeMethod: CodeChallengeMethod.SHA256, codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', scope: 'openid TestCredential', @@ -44,7 +43,6 @@ describe('OpenID4VCIClient should', () => { it('throw an error if authorization endpoint is not set in server metadata', async () => { expect(() => { client.createAuthorizationRequestUrl({ - clientId: 'test-client', codeChallengeMethod: CodeChallengeMethod.SHA256, codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', scope: 'openid TestCredential', @@ -58,7 +56,6 @@ describe('OpenID4VCIClient should', () => { client._endpointMetadata?.credentialIssuerMetadata.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`; const url = client.createAuthorizationRequestUrl({ - clientId: 'test-client', codeChallengeMethod: CodeChallengeMethod.SHA256, codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', scope: 'TestCredential', @@ -77,7 +74,6 @@ describe('OpenID4VCIClient should', () => { expect(() => { client.createAuthorizationRequestUrl({ - clientId: 'test-client', codeChallengeMethod: CodeChallengeMethod.SHA256, codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', redirectUri: 'http://localhost:8881/cb', @@ -91,7 +87,6 @@ describe('OpenID4VCIClient should', () => { expect( client.createAuthorizationRequestUrl({ - clientId: 'test-client', codeChallengeMethod: CodeChallengeMethod.SHA256, codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', authorizationDetails: [ @@ -112,7 +107,7 @@ describe('OpenID4VCIClient should', () => { redirectUri: 'http://localhost:8881/cb', }), ).toEqual( - 'https://server.example.com/v1/auth/authorize?response_type=code&client_id=test-client&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%5B%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%7D%2C%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22mso_mdoc%22%2C%22doctype%22%3A%22org%2Eiso%2E18013%2E5%2E1%2EmDL%22%2C%22locations%22%3A%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%7D%5D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb', + 'https://server.example.com/v1/auth/authorize?response_type=code&client_id=test-client&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%5B%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%7D%2C%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22mso_mdoc%22%2C%22doctype%22%3A%22org%2Eiso%2E18013%2E5%2E1%2EmDL%22%2C%22locations%22%3A%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%7D%5D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&scope=openid', ); }); it('create an authorization request url with authorization_details object property', async () => { @@ -122,7 +117,6 @@ describe('OpenID4VCIClient should', () => { expect( client.createAuthorizationRequestUrl({ - clientId: 'test-client', codeChallengeMethod: CodeChallengeMethod.SHA256, codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', authorizationDetails: { @@ -136,7 +130,7 @@ describe('OpenID4VCIClient should', () => { redirectUri: 'http://localhost:8881/cb', }), ).toEqual( - 'https://server.example.com/v1/auth/authorize?response_type=code&client_id=test-client&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%7D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb', + 'https://server.example.com/v1/auth/authorize?response_type=code&client_id=test-client&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%7D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&scope=openid', ); }); it('create an authorization request url with authorization_details and scope', async () => { @@ -146,7 +140,6 @@ describe('OpenID4VCIClient should', () => { expect( client.createAuthorizationRequestUrl({ - clientId: 'test-client', codeChallengeMethod: CodeChallengeMethod.SHA256, codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', authorizationDetails: { diff --git a/packages/client/lib/__tests__/OpenID4VCIClientPAR.spec.ts b/packages/client/lib/__tests__/OpenID4VCIClientPAR.spec.ts index 8a7e8ee6..bad3da3c 100644 --- a/packages/client/lib/__tests__/OpenID4VCIClientPAR.spec.ts +++ b/packages/client/lib/__tests__/OpenID4VCIClientPAR.spec.ts @@ -1,4 +1,4 @@ -import { AuthzFlowType, CodeChallengeMethod, WellKnownEndpoints } from '@sphereon/oid4vci-common'; +import { CodeChallengeMethod, WellKnownEndpoints } from '@sphereon/oid4vci-common'; import nock from 'nock'; import { OpenID4VCIClient } from '../OpenID4VCIClient'; @@ -13,8 +13,8 @@ describe('OpenID4VCIClient', () => { nock(MOCK_URL).get(WellKnownEndpoints.OPENID_CONFIGURATION).reply(404, {}); nock(`${MOCK_URL}`).post('/v1/auth/par').reply(201, { request_uri: 'test_uri', expires_in: 90 }); client = await OpenID4VCIClient.fromURI({ + clientId: 'test-client', uri: 'openid-initiate-issuance://?issuer=https://server.example.com&credential_type=TestCredential', - flowType: AuthzFlowType.AUTHORIZATION_CODE_FLOW, }); }); @@ -24,20 +24,19 @@ describe('OpenID4VCIClient', () => { it('should successfully retrieve the authorization code using PAR', async () => { client.endpointMetadata.credentialIssuerMetadata!.pushed_authorization_request_endpoint = `${MOCK_URL}v1/auth/par`; + client.endpointMetadata.credentialIssuerMetadata!.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`; const actual = await client.acquirePushedAuthorizationRequestURI({ - clientId: 'test-client', codeChallengeMethod: CodeChallengeMethod.SHA256, codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', scope: 'openid TestCredential', redirectUri: 'http://localhost:8881/cb', }); - expect(actual.successBody).toEqual({ request_uri: 'test_uri', expires_in: 90 }); + expect(actual).toEqual('https://server.example.com/v1/auth/authorize?request_uri=test_uri'); }); it('should fail when pushed_authorization_request_endpoint is not present', async () => { await expect(() => client.acquirePushedAuthorizationRequestURI({ - clientId: 'test-client', codeChallengeMethod: CodeChallengeMethod.SHA256, codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', scope: 'openid TestCredential', @@ -49,7 +48,6 @@ describe('OpenID4VCIClient', () => { it('should fail when authorization_details and scope are not present', async () => { await expect(() => client.acquirePushedAuthorizationRequestURI({ - clientId: 'test-client', codeChallengeMethod: CodeChallengeMethod.SHA256, codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', redirectUri: 'http://localhost:8881/cb', @@ -59,8 +57,8 @@ describe('OpenID4VCIClient', () => { it('should not fail when only authorization_details is present', async () => { client.endpointMetadata.credentialIssuerMetadata!.pushed_authorization_request_endpoint = `${MOCK_URL}v1/auth/par`; + client.endpointMetadata.credentialIssuerMetadata!.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`; const actual = await client.acquirePushedAuthorizationRequestURI({ - clientId: 'test-client', codeChallengeMethod: CodeChallengeMethod.SHA256, codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', authorizationDetails: [ @@ -75,25 +73,25 @@ describe('OpenID4VCIClient', () => { ], redirectUri: 'http://localhost:8881/cb', }); - expect(actual.successBody).toEqual({ request_uri: 'test_uri', expires_in: 90 }); + expect(actual).toEqual('https://server.example.com/v1/auth/authorize?request_uri=test_uri'); }); it('should not fail when only scope is present', async () => { client.endpointMetadata.credentialIssuerMetadata!.pushed_authorization_request_endpoint = `${MOCK_URL}v1/auth/par`; + client.endpointMetadata.credentialIssuerMetadata!.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`; const actual = await client.acquirePushedAuthorizationRequestURI({ - clientId: 'test-client', codeChallengeMethod: CodeChallengeMethod.SHA256, codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', scope: 'openid TestCredential', redirectUri: 'http://localhost:8881/cb', }); - expect(actual.successBody).toEqual({ request_uri: 'test_uri', expires_in: 90 }); + expect(actual).toEqual('https://server.example.com/v1/auth/authorize?request_uri=test_uri'); }); it('should not fail when both authorization_details and scope are present', async () => { client.endpointMetadata.credentialIssuerMetadata!.pushed_authorization_request_endpoint = `${MOCK_URL}v1/auth/par`; + client.endpointMetadata.credentialIssuerMetadata!.authorization_endpoint = `${MOCK_URL}v1/auth/authorize`; const actual = await client.acquirePushedAuthorizationRequestURI({ - clientId: 'test-client', codeChallengeMethod: CodeChallengeMethod.SHA256, codeChallenge: 'mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs', authorizationDetails: [ @@ -109,6 +107,6 @@ describe('OpenID4VCIClient', () => { scope: 'openid TestCredential', redirectUri: 'http://localhost:8881/cb', }); - expect(actual.successBody).toEqual({ request_uri: 'test_uri', expires_in: 90 }); + expect(actual).toEqual('https://server.example.com/v1/auth/authorize?request_uri=test_uri'); }); }); diff --git a/packages/common/lib/functions/Encoding.ts b/packages/common/lib/functions/Encoding.ts index 3e789765..b84a918a 100644 --- a/packages/common/lib/functions/Encoding.ts +++ b/packages/common/lib/functions/Encoding.ts @@ -1,4 +1,4 @@ -import { BAD_PARAMS, DecodeURIAsJsonOpts, EncodeJsonAsURIOpts, SearchValue } from '../types'; +import { BAD_PARAMS, DecodeURIAsJsonOpts, EncodeJsonAsURIOpts, OpenId4VCIVersion, SearchValue } from '../types'; /** * @function encodeJsonAsURI encodes a Json object into a URI @@ -29,35 +29,39 @@ export function convertJsonToURI( return encodeURIComponent(key.replace(' ', '')); } - for (const [key, value] of Object.entries(json)) { - if (!value) { - continue; - } - //Skip properties that are not of URL type - if (!opts?.uriTypeProperties?.includes(key)) { - results.push(`${key}=${value}`); - continue; - } - if (opts?.arrayTypeProperties?.includes(key) && Array.isArray(value)) { - results.push(value.map((v) => `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(v, /\./g)}`).join('&')); - continue; - } - const isBool = typeof value == 'boolean'; - const isNumber = typeof value == 'number'; - const isString = typeof value == 'string'; - let encoded; - if (isBool || isNumber) { - encoded = `${encodeAndStripWhitespace(key)}=${value}`; - } else if (isString) { - encoded = `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(value, /\./g)}`; - } else { - encoded = `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(JSON.stringify(value), /\./g)}`; + let components: string; + if (opts?.version && opts.version > OpenId4VCIVersion.VER_1_0_08) { + // v11 changed from encoding every param to a encoded json object with a credential_offer param key + components = encodeAndStripWhitespace(JSON.stringify(json)); + } else { + for (const [key, value] of Object.entries(json)) { + if (!value) { + continue; + } + //Skip properties that are not of URL type + if (!opts?.uriTypeProperties?.includes(key)) { + results.push(`${key}=${value}`); + continue; + } + if (opts?.arrayTypeProperties?.includes(key) && Array.isArray(value)) { + results.push(value.map((v) => `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(v, /\./g)}`).join('&')); + continue; + } + const isBool = typeof value == 'boolean'; + const isNumber = typeof value == 'number'; + const isString = typeof value == 'string'; + let encoded; + if (isBool || isNumber) { + encoded = `${encodeAndStripWhitespace(key)}=${value}`; + } else if (isString) { + encoded = `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(value, /\./g)}`; + } else { + encoded = `${encodeAndStripWhitespace(key)}=${customEncodeURIComponent(JSON.stringify(value), /\./g)}`; + } + results.push(encoded); } - results.push(encoded); + components = results.join('&'); } - - const components = results.join('&'); - if (opts?.baseUrl) { if (opts.baseUrl.endsWith('=')) { if (opts.param) { diff --git a/packages/issuer-rest/lib/__tests__/ClientIssuerIT.spec.ts b/packages/issuer-rest/lib/__tests__/ClientIssuerIT.spec.ts index 6364a7b6..c19316be 100644 --- a/packages/issuer-rest/lib/__tests__/ClientIssuerIT.spec.ts +++ b/packages/issuer-rest/lib/__tests__/ClientIssuerIT.spec.ts @@ -5,7 +5,6 @@ import { OpenID4VCIClient } from '@sphereon/oid4vci-client' import { AccessTokenResponse, Alg, - AuthzFlowType, CredentialOfferSession, CredentialSupported, IssuerCredentialSubjectDisplay, @@ -207,7 +206,6 @@ describe('VcIssuer', () => { it('should create client from credential offer URI', async () => { client = await OpenID4VCIClient.fromURI({ uri, - flowType: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW, kid: subjectDIDKey.didDocument.authentication[0], alg: 'ES256', }) diff --git a/packages/issuer/lib/__tests__/VcIssuer.spec.ts b/packages/issuer/lib/__tests__/VcIssuer.spec.ts index 4ff68ce6..dd5d9e97 100644 --- a/packages/issuer/lib/__tests__/VcIssuer.spec.ts +++ b/packages/issuer/lib/__tests__/VcIssuer.spec.ts @@ -1,7 +1,6 @@ import { OpenID4VCIClient } from '@sphereon/oid4vci-client' import { Alg, - AuthzFlowType, CredentialOfferLdpVcV1_0_11, CredentialOfferSession, CredentialSupported, @@ -153,7 +152,7 @@ describe('VcIssuer', () => { 'http://issuer-example.com?credential_offer=%7B%22grants%22%3A%7B%22authorization_code%22%3A%7B%22issuer_state%22%3A%22previously-created-state%22%7D%2C%22urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Apre-authorized_code%22%3A%7B%22pre-authorized_code%22%3A%22test_code%22%2C%22user_pin_required%22%3Atrue%7D%7D%2C%22credential_issuer%22%3A%22https%3A%2F%2Fissuer.research.identiproof.io%22%2C%22credentials%22%3A%5B%7B%22format%22%3A%22jwt_vc_json%22%2C%22types%22%3A%5B%22VerifiableCredential%22%5D%2C%22credentialSubject%22%3A%7B%22given_name%22%3A%7B%22name%22%3A%22given%20name%22%2C%22locale%22%3A%22en-US%22%7D%7D%2C%22cryptographic_suites_supported%22%3A%5B%22ES256K%22%5D%2C%22cryptographic_binding_methods_supported%22%3A%5B%22did%22%5D%2C%22id%22%3A%22UniversityDegree_JWT%22%2C%22display%22%3A%5B%7B%22name%22%3A%22University%20Credential%22%2C%22locale%22%3A%22en-US%22%2C%22logo%22%3A%7B%22url%22%3A%22https%3A%2F%2Fexampleuniversity.com%2Fpublic%2Flogo.png%22%2C%22alt_text%22%3A%22a%20square%20logo%20of%20a%20university%22%7D%2C%22background_color%22%3A%22%2312107c%22%2C%22text_color%22%3A%22%23FFFFFF%22%7D%5D%7D%5D%7D', ) - const client = await OpenID4VCIClient.fromURI({ uri, flowType: AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW }) + const client = await OpenID4VCIClient.fromURI({ uri }) expect(client.credentialOffer).toEqual({ baseUrl: 'http://issuer-example.com', credential_offer: { From a73afee4c93905a9e33dff9dd9e4f798646d8d64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Linas=20Is=CC=8Cganaitis?= Date: Sat, 7 Oct 2023 23:14:23 +0200 Subject: [PATCH 3/4] Add client_id only if it was provided during client initialization --- packages/client/lib/OpenID4VCIClient.ts | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/packages/client/lib/OpenID4VCIClient.ts b/packages/client/lib/OpenID4VCIClient.ts index fe90709a..53aa2eaa 100644 --- a/packages/client/lib/OpenID4VCIClient.ts +++ b/packages/client/lib/OpenID4VCIClient.ts @@ -116,7 +116,6 @@ export class OpenID4VCIClient { const queryObj: { [key: string]: string } = { response_type: ResponseType.AUTH_CODE, - client_id: this.clientId || '', code_challenge_method: codeChallengeMethod, code_challenge: codeChallenge, authorization_details: JSON.stringify(this.handleAuthorizationDetails(authorizationDetails)), @@ -124,6 +123,10 @@ export class OpenID4VCIClient { scope: scope, }; + if (this.clientId) { + queryObj['client_id'] = this.clientId; + } + if (this.credentialOffer.issuerState) { queryObj['issuer_state'] = this.credentialOffer.issuerState; } @@ -167,7 +170,6 @@ export class OpenID4VCIClient { const queryObj: { [key: string]: string } = { response_type: ResponseType.AUTH_CODE, - client_id: this.clientId || '', code_challenge_method: codeChallengeMethod, code_challenge: codeChallenge, authorization_details: JSON.stringify(this.handleAuthorizationDetails(authorizationDetails)), @@ -175,6 +177,10 @@ export class OpenID4VCIClient { scope: scope, }; + if (this.clientId) { + queryObj['client_id'] = this.clientId; + } + if (this.credentialOffer.issuerState) { queryObj['issuer_state'] = this.credentialOffer.issuerState; } From 1114e8308ee421aeb08fda65d1bc311182a2f95a Mon Sep 17 00:00:00 2001 From: Niels Klomp Date: Sun, 15 Oct 2023 00:29:21 +0200 Subject: [PATCH 4/4] chore: Add mode option to override how encodeJsonToURI should function, not only depending on versions, but also allowing to provide the mode explicitly. refs #70 --- packages/callback-example/CHANGELOG.md | 15 +----------- packages/client/CHANGELOG.md | 24 ++++--------------- packages/client/lib/OpenID4VCIClient.ts | 4 ++++ .../lib/__tests__/OpenID4VCIClient.spec.ts | 6 ++--- packages/common/CHANGELOG.md | 24 ++++--------------- packages/common/lib/functions/Encoding.ts | 5 ++-- .../lib/types/CredentialIssuance.types.ts | 6 +++++ packages/common/lib/types/Generic.types.ts | 2 +- packages/issuer-rest/CHANGELOG.md | 18 ++------------ packages/issuer/CHANGELOG.md | 17 ++----------- packages/issuer/lib/VcIssuer.ts | 8 +++---- packages/issuer/lib/types/index.ts | 5 ++-- 12 files changed, 39 insertions(+), 95 deletions(-) diff --git a/packages/callback-example/CHANGELOG.md b/packages/callback-example/CHANGELOG.md index 560d36af..7c120756 100644 --- a/packages/callback-example/CHANGELOG.md +++ b/packages/callback-example/CHANGELOG.md @@ -7,28 +7,15 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline **Note:** Version bump only for package @sphereon/oid4vci-callback-example - - - - ## [0.7.2](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.7.1...v0.7.2) (2023-09-28) **Note:** Version bump only for package @sphereon/oid4vci-callback-example - - - - ## [0.7.1](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.7.0...v0.7.1) (2023-09-28) - ### Bug Fixes -* Better match credential offer types and formats onto issuer metadata ([4044c21](https://github.com/Sphereon-Opensource/OID4VCI/commit/4044c2175b4cbee16f44c8bb5499bba249ca4993)) - - - - +- Better match credential offer types and formats onto issuer metadata ([4044c21](https://github.com/Sphereon-Opensource/OID4VCI/commit/4044c2175b4cbee16f44c8bb5499bba249ca4993)) # [0.7.0](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.6.0...v0.7.0) (2023-08-19) diff --git a/packages/client/CHANGELOG.md b/packages/client/CHANGELOG.md index a8623855..ce123930 100644 --- a/packages/client/CHANGELOG.md +++ b/packages/client/CHANGELOG.md @@ -7,34 +7,20 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline **Note:** Version bump only for package @sphereon/oid4vci-client - - - - ## [0.7.2](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.7.1...v0.7.2) (2023-09-28) - ### Bug Fixes -* id lookup against server metadata not working ([592ec4b](https://github.com/Sphereon-Opensource/OID4VCI/commit/592ec4b837898eb3022d19479d79b6065e7a0d9e)) - - - - +- id lookup against server metadata not working ([592ec4b](https://github.com/Sphereon-Opensource/OID4VCI/commit/592ec4b837898eb3022d19479d79b6065e7a0d9e)) ## [0.7.1](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.7.0...v0.7.1) (2023-09-28) - ### Bug Fixes -* Better match credential offer types and formats onto issuer metadata ([4044c21](https://github.com/Sphereon-Opensource/OID4VCI/commit/4044c2175b4cbee16f44c8bb5499bba249ca4993)) -* clearinterval ([214e3c6](https://github.com/Sphereon-Opensource/OID4VCI/commit/214e3c6d7ced9b27c50186db8ed876330230a6a5)) -* relax auth_endpoint handling. Doesn't have to be available when doing pre-auth flow. Client handles errors anyway in case of auth/par flow ([ce39958](https://github.com/Sphereon-Opensource/OID4VCI/commit/ce39958f21f82243f26111fd14bd2443517eef9c)) -* relax auth_endpoint handling. Doesn't have to be available when doing pre-auth flow. Client handles errors anyway in case of auth/par flow ([cb5f9c1](https://github.com/Sphereon-Opensource/OID4VCI/commit/cb5f9c1c12285508c6d403814d032e8883a59e7d)) - - - - +- Better match credential offer types and formats onto issuer metadata ([4044c21](https://github.com/Sphereon-Opensource/OID4VCI/commit/4044c2175b4cbee16f44c8bb5499bba249ca4993)) +- clearinterval ([214e3c6](https://github.com/Sphereon-Opensource/OID4VCI/commit/214e3c6d7ced9b27c50186db8ed876330230a6a5)) +- relax auth_endpoint handling. Doesn't have to be available when doing pre-auth flow. Client handles errors anyway in case of auth/par flow ([ce39958](https://github.com/Sphereon-Opensource/OID4VCI/commit/ce39958f21f82243f26111fd14bd2443517eef9c)) +- relax auth_endpoint handling. Doesn't have to be available when doing pre-auth flow. Client handles errors anyway in case of auth/par flow ([cb5f9c1](https://github.com/Sphereon-Opensource/OID4VCI/commit/cb5f9c1c12285508c6d403814d032e8883a59e7d)) # [0.7.0](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.6.0...v0.7.0) (2023-08-19) diff --git a/packages/client/lib/OpenID4VCIClient.ts b/packages/client/lib/OpenID4VCIClient.ts index 53aa2eaa..90aa0955 100644 --- a/packages/client/lib/OpenID4VCIClient.ts +++ b/packages/client/lib/OpenID4VCIClient.ts @@ -8,6 +8,7 @@ import { CredentialResponse, CredentialSupported, EndpointMetadataResult, + JsonURIMode, OID4VCICredentialFormat, OpenId4VCIVersion, ProofOfPossessionCallbacks, @@ -134,6 +135,8 @@ export class OpenID4VCIClient { return convertJsonToURI(queryObj, { baseUrl: this._endpointMetadata.authorization_endpoint, uriTypeProperties: ['redirect_uri', 'scope', 'authorization_details', 'issuer_state'], + mode: JsonURIMode.X_FORM_WWW_URLENCODED, + // We do not add the version here, as this always needs to be form encoded }); } @@ -192,6 +195,7 @@ export class OpenID4VCIClient { { baseUrl: this._endpointMetadata.credentialIssuerMetadata.authorization_endpoint, uriTypeProperties: ['request_uri'], + mode: JsonURIMode.X_FORM_WWW_URLENCODED, }, ); } diff --git a/packages/client/lib/__tests__/OpenID4VCIClient.spec.ts b/packages/client/lib/__tests__/OpenID4VCIClient.spec.ts index 4524f30e..d29100bd 100644 --- a/packages/client/lib/__tests__/OpenID4VCIClient.spec.ts +++ b/packages/client/lib/__tests__/OpenID4VCIClient.spec.ts @@ -107,7 +107,7 @@ describe('OpenID4VCIClient should', () => { redirectUri: 'http://localhost:8881/cb', }), ).toEqual( - 'https://server.example.com/v1/auth/authorize?response_type=code&client_id=test-client&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%5B%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%7D%2C%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22mso_mdoc%22%2C%22doctype%22%3A%22org%2Eiso%2E18013%2E5%2E1%2EmDL%22%2C%22locations%22%3A%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%7D%5D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&scope=openid', + 'https://server.example.com/v1/auth/authorize?response_type=code&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%5B%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%7D%2C%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22mso_mdoc%22%2C%22doctype%22%3A%22org%2Eiso%2E18013%2E5%2E1%2EmDL%22%2C%22locations%22%3A%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%7D%5D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&scope=openid&client_id=test-client', ); }); it('create an authorization request url with authorization_details object property', async () => { @@ -130,7 +130,7 @@ describe('OpenID4VCIClient should', () => { redirectUri: 'http://localhost:8881/cb', }), ).toEqual( - 'https://server.example.com/v1/auth/authorize?response_type=code&client_id=test-client&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%7D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&scope=openid', + 'https://server.example.com/v1/auth/authorize?response_type=code&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%2C%22locations%22%3A%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%7D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&scope=openid&client_id=test-client', ); }); it('create an authorization request url with authorization_details and scope', async () => { @@ -155,7 +155,7 @@ describe('OpenID4VCIClient should', () => { redirectUri: 'http://localhost:8881/cb', }), ).toEqual( - 'https://server.example.com/v1/auth/authorize?response_type=code&client_id=test-client&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22locations%22%3A%5B%22https%3A%2F%2Ftest%2Ecom%22%2C%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%7D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&scope=openid', + 'https://server.example.com/v1/auth/authorize?response_type=code&code_challenge_method=S256&code_challenge=mE2kPHmIprOqtkaYmESWj35yz-PB5vzdiSu0tAZ8sqs&authorization_details=%7B%22type%22%3A%22openid_credential%22%2C%22format%22%3A%22ldp_vc%22%2C%22locations%22%3A%5B%22https%3A%2F%2Ftest%2Ecom%22%2C%22https%3A%2F%2Fserver%2Eexample%2Ecom%22%5D%2C%22credential_definition%22%3A%7B%22%40context%22%3A%5B%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fv1%22%2C%22https%3A%2F%2Fwww%2Ew3%2Eorg%2F2018%2Fcredentials%2Fexamples%2Fv1%22%5D%2C%22types%22%3A%5B%22VerifiableCredential%22%2C%22UniversityDegreeCredential%22%5D%7D%7D&redirect_uri=http%3A%2F%2Flocalhost%3A8881%2Fcb&scope=openid&client_id=test-client', ); }); }); diff --git a/packages/common/CHANGELOG.md b/packages/common/CHANGELOG.md index e5f23567..04271689 100644 --- a/packages/common/CHANGELOG.md +++ b/packages/common/CHANGELOG.md @@ -7,34 +7,20 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline **Note:** Version bump only for package @sphereon/oid4vci-common - - - - ## [0.7.2](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.7.1...v0.7.2) (2023-09-28) - ### Bug Fixes -* id lookup against server metadata not working ([592ec4b](https://github.com/Sphereon-Opensource/OID4VCI/commit/592ec4b837898eb3022d19479d79b6065e7a0d9e)) - - - - +- id lookup against server metadata not working ([592ec4b](https://github.com/Sphereon-Opensource/OID4VCI/commit/592ec4b837898eb3022d19479d79b6065e7a0d9e)) ## [0.7.1](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.7.0...v0.7.1) (2023-09-28) - ### Bug Fixes -* Better match credential offer types and formats onto issuer metadata ([4044c21](https://github.com/Sphereon-Opensource/OID4VCI/commit/4044c2175b4cbee16f44c8bb5499bba249ca4993)) -* Fix credential offer matching against metadata ([3c23bab](https://github.com/Sphereon-Opensource/OID4VCI/commit/3c23bab83569e04a4b5846fed83ce00d68e8ddce)) -* Fix credential offer matching against metadata ([b79027f](https://github.com/Sphereon-Opensource/OID4VCI/commit/b79027fe601ecccb1373ba399419e14f5ec2d7ff)) -* relax auth_endpoint handling. Doesn't have to be available when doing pre-auth flow. Client handles errors anyway in case of auth/par flow ([cb5f9c1](https://github.com/Sphereon-Opensource/OID4VCI/commit/cb5f9c1c12285508c6d403814d032e8883a59e7d)) - - - - +- Better match credential offer types and formats onto issuer metadata ([4044c21](https://github.com/Sphereon-Opensource/OID4VCI/commit/4044c2175b4cbee16f44c8bb5499bba249ca4993)) +- Fix credential offer matching against metadata ([3c23bab](https://github.com/Sphereon-Opensource/OID4VCI/commit/3c23bab83569e04a4b5846fed83ce00d68e8ddce)) +- Fix credential offer matching against metadata ([b79027f](https://github.com/Sphereon-Opensource/OID4VCI/commit/b79027fe601ecccb1373ba399419e14f5ec2d7ff)) +- relax auth_endpoint handling. Doesn't have to be available when doing pre-auth flow. Client handles errors anyway in case of auth/par flow ([cb5f9c1](https://github.com/Sphereon-Opensource/OID4VCI/commit/cb5f9c1c12285508c6d403814d032e8883a59e7d)) # [0.7.0](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.6.0...v0.7.0) (2023-08-19) diff --git a/packages/common/lib/functions/Encoding.ts b/packages/common/lib/functions/Encoding.ts index b84a918a..e9310e29 100644 --- a/packages/common/lib/functions/Encoding.ts +++ b/packages/common/lib/functions/Encoding.ts @@ -1,4 +1,4 @@ -import { BAD_PARAMS, DecodeURIAsJsonOpts, EncodeJsonAsURIOpts, OpenId4VCIVersion, SearchValue } from '../types'; +import { BAD_PARAMS, DecodeURIAsJsonOpts, EncodeJsonAsURIOpts, JsonURIMode, OpenId4VCIVersion, SearchValue } from '../types'; /** * @function encodeJsonAsURI encodes a Json object into a URI @@ -30,10 +30,11 @@ export function convertJsonToURI( } let components: string; - if (opts?.version && opts.version > OpenId4VCIVersion.VER_1_0_08) { + if ((opts?.version && opts.version > OpenId4VCIVersion.VER_1_0_08 && !opts.mode) || opts?.mode === JsonURIMode.JSON_STRINGIFY) { // v11 changed from encoding every param to a encoded json object with a credential_offer param key components = encodeAndStripWhitespace(JSON.stringify(json)); } else { + // version 8 or lower, or mode is x-form-www-urlencoded for (const [key, value] of Object.entries(json)) { if (!value) { continue; diff --git a/packages/common/lib/types/CredentialIssuance.types.ts b/packages/common/lib/types/CredentialIssuance.types.ts index 0e45a56e..bf43a4cf 100644 --- a/packages/common/lib/types/CredentialIssuance.types.ts +++ b/packages/common/lib/types/CredentialIssuance.types.ts @@ -56,11 +56,17 @@ export type SearchValue = { [Symbol.replace](string: string, replacer: (substring: string, ...args: any[]) => string): string; }; +export enum JsonURIMode { + JSON_STRINGIFY, + X_FORM_WWW_URLENCODED, +} + export type EncodeJsonAsURIOpts = { uriTypeProperties?: string[]; arrayTypeProperties?: string[]; baseUrl?: string; param?: string; + mode?: JsonURIMode; version?: OpenId4VCIVersion; }; diff --git a/packages/common/lib/types/Generic.types.ts b/packages/common/lib/types/Generic.types.ts index 381aa0b7..4266cc09 100644 --- a/packages/common/lib/types/Generic.types.ts +++ b/packages/common/lib/types/Generic.types.ts @@ -53,7 +53,7 @@ export interface CredentialIssuerMetadataOpts { authorization_server?: string; // OPTIONAL. Identifier of the OAuth 2.0 Authorization Server (as defined in [RFC8414]) the Credential Issuer relies on for authorization. If this element is omitted, the entity providing the Credential Issuer is also acting as the AS, i.e. the Credential Issuer's identifier is used as the OAuth 2.0 Issuer value to obtain the Authorization Server metadata as per [RFC8414]. token_endpoint?: string; display?: MetadataDisplay[]; // An array of objects, where each object contains display properties of a Credential Issuer for a certain language. Below is a non-exhaustive list of valid parameters that MAY be included: - credential_supplier_config?: CredentialSupplierConfig + credential_supplier_config?: CredentialSupplierConfig; } // For now we extend the opts above. Only difference is that the credential endpoint is optional in the Opts, as it can come from other sources. The value is however required in the eventual Issuer Metadata diff --git a/packages/issuer-rest/CHANGELOG.md b/packages/issuer-rest/CHANGELOG.md index 21d8261e..01c90b1f 100644 --- a/packages/issuer-rest/CHANGELOG.md +++ b/packages/issuer-rest/CHANGELOG.md @@ -5,33 +5,19 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline ## [0.7.3](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.7.2...v0.7.3) (2023-09-30) - ### Bug Fixes -* allow token endpoint to be defined in metadata without triggering logic for external AS ([d99304c](https://github.com/Sphereon-Opensource/OID4VCI/commit/d99304cd02b92974785f516e8bd82900cc3e0925)) - - - - +- allow token endpoint to be defined in metadata without triggering logic for external AS ([d99304c](https://github.com/Sphereon-Opensource/OID4VCI/commit/d99304cd02b92974785f516e8bd82900cc3e0925)) ## [0.7.2](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.7.1...v0.7.2) (2023-09-28) **Note:** Version bump only for package @sphereon/oid4vci-issuer-server - - - - ## [0.7.1](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.7.0...v0.7.1) (2023-09-28) - ### Bug Fixes -* Better match credential offer types and formats onto issuer metadata ([4044c21](https://github.com/Sphereon-Opensource/OID4VCI/commit/4044c2175b4cbee16f44c8bb5499bba249ca4993)) - - - - +- Better match credential offer types and formats onto issuer metadata ([4044c21](https://github.com/Sphereon-Opensource/OID4VCI/commit/4044c2175b4cbee16f44c8bb5499bba249ca4993)) # [0.7.0](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.6.0...v0.7.0) (2023-08-19) diff --git a/packages/issuer/CHANGELOG.md b/packages/issuer/CHANGELOG.md index 685d8f03..ade20f70 100644 --- a/packages/issuer/CHANGELOG.md +++ b/packages/issuer/CHANGELOG.md @@ -7,29 +7,16 @@ See [Conventional Commits](https://conventionalcommits.org) for commit guideline **Note:** Version bump only for package @sphereon/oid4vci-issuer - - - - ## [0.7.2](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.7.1...v0.7.2) (2023-09-28) **Note:** Version bump only for package @sphereon/oid4vci-issuer - - - - ## [0.7.1](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.7.0...v0.7.1) (2023-09-28) - ### Bug Fixes -* Better match credential offer types and formats onto issuer metadata ([4044c21](https://github.com/Sphereon-Opensource/OID4VCI/commit/4044c2175b4cbee16f44c8bb5499bba249ca4993)) -* clearinterval ([214e3c6](https://github.com/Sphereon-Opensource/OID4VCI/commit/214e3c6d7ced9b27c50186db8ed876330230a6a5)) - - - - +- Better match credential offer types and formats onto issuer metadata ([4044c21](https://github.com/Sphereon-Opensource/OID4VCI/commit/4044c2175b4cbee16f44c8bb5499bba249ca4993)) +- clearinterval ([214e3c6](https://github.com/Sphereon-Opensource/OID4VCI/commit/214e3c6d7ced9b27c50186db8ed876330230a6a5)) # [0.7.0](https://github.com/Sphereon-Opensource/OID4VCI/compare/v0.6.0...v0.7.0) (2023-08-19) diff --git a/packages/issuer/lib/VcIssuer.ts b/packages/issuer/lib/VcIssuer.ts index baab7400..054ceb82 100644 --- a/packages/issuer/lib/VcIssuer.ts +++ b/packages/issuer/lib/VcIssuer.ts @@ -276,11 +276,11 @@ export class VcIssuer { } const credentialDataSupplierInput = opts.credentialDataSupplierInput ?? session.credentialDataSupplierInput - const result = await credentialDataSupplier({ + const result = await credentialDataSupplier({ ...cNonceState, - credentialRequest: opts.credentialRequest, - credentialSupplierConfig: this._issuerMetadata.credential_supplier_config, - credentialOffer /*todo: clientId: */, + credentialRequest: opts.credentialRequest, + credentialSupplierConfig: this._issuerMetadata.credential_supplier_config, + credentialOffer /*todo: clientId: */, ...(credentialDataSupplierInput && { credentialDataSupplierInput }), } as CredentialDataSupplierArgs) credential = result.credential diff --git a/packages/issuer/lib/types/index.ts b/packages/issuer/lib/types/index.ts index 3582de42..71fc1ba3 100644 --- a/packages/issuer/lib/types/index.ts +++ b/packages/issuer/lib/types/index.ts @@ -1,10 +1,11 @@ import { AssertedUniformCredentialOffer, CNonceState, - CredentialDataSupplierInput, CredentialSupplierConfig, + CredentialDataSupplierInput, + CredentialSupplierConfig, JwtVerifyResult, OID4VCICredentialFormat, - UniformCredentialRequest + UniformCredentialRequest, } from '@sphereon/oid4vci-common' import { ICredential, W3CVerifiableCredential } from '@sphereon/ssi-types'