From 5aeff03cce285d29c105aeca2c234269ca4999e2 Mon Sep 17 00:00:00 2001 From: Sadjad Khoshkhou Date: Thu, 19 Sep 2024 13:11:22 +0200 Subject: [PATCH 1/6] fix: small fixes for siop-oid4vp package --- packages/did-auth-siop-adapter/lib/did/DIDResolution.ts | 2 +- packages/did-auth-siop-adapter/lib/did/DidJWT.ts | 2 +- .../lib/__tests__/AuthenticationResponse.response.spec.ts | 4 ++-- .../lib/authorization-response/PresentationExchange.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/did-auth-siop-adapter/lib/did/DIDResolution.ts b/packages/did-auth-siop-adapter/lib/did/DIDResolution.ts index 9eb1b354..ab6b19c6 100644 --- a/packages/did-auth-siop-adapter/lib/did/DIDResolution.ts +++ b/packages/did-auth-siop-adapter/lib/did/DIDResolution.ts @@ -113,5 +113,5 @@ export async function resolveDidDocument(did: string, opts?: ResolveOpts): Promi // todo: This looks like a bug. It seems that sometimes we get back a DID document directly instead of a did resolution results return result as unknown as DIDDocument } - return result.didDocument + return result.didDocument as DIDDocument } diff --git a/packages/did-auth-siop-adapter/lib/did/DidJWT.ts b/packages/did-auth-siop-adapter/lib/did/DidJWT.ts index be2f786e..9a671d7a 100644 --- a/packages/did-auth-siop-adapter/lib/did/DidJWT.ts +++ b/packages/did-auth-siop-adapter/lib/did/DidJWT.ts @@ -259,7 +259,7 @@ export function getSubDidFromPayload(payload: JWTPayload, header?: JWTHeader): s export function isIssSelfIssued(payload: JWTPayload): boolean { return ( (payload.iss && payload.iss.includes(ResponseIss.SELF_ISSUED_V1)) || - payload.iss.includes(ResponseIss.SELF_ISSUED_V2) || + (payload.iss && payload.iss.includes(ResponseIss.SELF_ISSUED_V2)) || payload.iss === payload.sub ) } diff --git a/packages/siop-oid4vp/lib/__tests__/AuthenticationResponse.response.spec.ts b/packages/siop-oid4vp/lib/__tests__/AuthenticationResponse.response.spec.ts index 47f4397f..fc330efd 100644 --- a/packages/siop-oid4vp/lib/__tests__/AuthenticationResponse.response.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/AuthenticationResponse.response.spec.ts @@ -329,7 +329,7 @@ describe('create JWT from Request JWT should', () => { }, ], constraints: { - limit_disclosure: 'required', + //limit_disclosure: 'required', fields: [ { path: ['$.issuer.id'], @@ -486,7 +486,7 @@ describe('create JWT from Request JWT should', () => { }, ], constraints: { - limit_disclosure: 'required', + // limit_disclosure: 'required', fields: [ { path: ['$.issuer.id'], diff --git a/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts b/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts index 726f3c6c..e04af13c 100644 --- a/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts +++ b/packages/siop-oid4vp/lib/authorization-response/PresentationExchange.ts @@ -320,7 +320,7 @@ export class PresentationExchange { throw new Error(SIOPErrors.NO_PRESENTATION_SUBMISSION) } - if (!evaluationResults.areRequiredCredentialsPresent || evaluationResults.errors || !evaluationResults.value) { + if (evaluationResults.areRequiredCredentialsPresent === Status.ERROR || evaluationResults.errors?.length || !evaluationResults.value) { throw new Error(`message: ${SIOPErrors.COULD_NOT_FIND_VCS_MATCHING_PD}, details: ${JSON.stringify(evaluationResults.errors)}`) } From 8584d766c065672bbd70c1c916bfb506b1004b53 Mon Sep 17 00:00:00 2001 From: Sadjad Khoshkhou Date: Thu, 19 Sep 2024 15:40:26 +0200 Subject: [PATCH 2/6] fix: small fixes for siop-oid4vp package --- .../lib/__tests__/e2e/mattr.launchpad.spec.ts | 3 +- .../AuthorizationRequest.ts | 43 +++++++------------ 2 files changed, 18 insertions(+), 28 deletions(-) diff --git a/packages/siop-oid4vp/lib/__tests__/e2e/mattr.launchpad.spec.ts b/packages/siop-oid4vp/lib/__tests__/e2e/mattr.launchpad.spec.ts index 63750dff..5a8368e2 100644 --- a/packages/siop-oid4vp/lib/__tests__/e2e/mattr.launchpad.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/e2e/mattr.launchpad.spec.ts @@ -135,7 +135,8 @@ describe('OID4VCI-Client using Mattr issuer should', () => { const correlationId = 'test' - const verifiedAuthRequest = await AuthorizationRequest.verify(authorizeRequestUri, { + const authorizationRequest = await AuthorizationRequest.fromUriOrJwt(offer.authorizeRequestUri); + const verifiedAuthRequest = await authorizationRequest.verify({ correlationId, verifyJwtCallback: getVerifyJwtCallback(getResolver()), verification: {}, diff --git a/packages/siop-oid4vp/lib/authorization-request/AuthorizationRequest.ts b/packages/siop-oid4vp/lib/authorization-request/AuthorizationRequest.ts index 89201ea8..eab6b4d1 100644 --- a/packages/siop-oid4vp/lib/authorization-request/AuthorizationRequest.ts +++ b/packages/siop-oid4vp/lib/authorization-request/AuthorizationRequest.ts @@ -30,8 +30,8 @@ import { CreateAuthorizationRequestOpts, VerifyAuthorizationRequestOpts } from ' export class AuthorizationRequest { private readonly _requestObject?: RequestObject private readonly _payload: AuthorizationRequestPayload - private readonly _options: CreateAuthorizationRequestOpts | undefined - private _uri: URI | undefined + private readonly _options: CreateAuthorizationRequestOpts + private _uri: URI private constructor(payload: AuthorizationRequestPayload, requestObject?: RequestObject, opts?: CreateAuthorizationRequestOpts, uri?: URI) { this._options = opts @@ -66,7 +66,7 @@ export class AuthorizationRequest { const requestObjectArg = opts.requestObject.passBy !== PassBy.NONE ? (requestObject ? requestObject : await RequestObject.fromOpts(opts)) : undefined - const requestPayload = await createAuthorizationRequestPayload(opts, requestObjectArg) + const requestPayload = opts?.payload ? await createAuthorizationRequestPayload(opts, requestObjectArg) : undefined return new AuthorizationRequest(requestPayload, requestObjectArg, opts) } @@ -116,13 +116,10 @@ export class AuthorizationRequest { async verify(opts: VerifyAuthorizationRequestOpts): Promise { assertValidVerifyAuthorizationRequestOpts(opts) - let requestObjectPayload: RequestObjectPayload | undefined + let requestObjectPayload: RequestObjectPayload | undefined = undefined const jwt = await this.requestObjectJwt() - if(jwt === undefined) { - return Promise.reject(Error('jwt could be fetched, request object unavailable')) - } - const parsedJwt = parseJWT(jwt) + const parsedJwt = jwt ? parseJWT(jwt) : undefined if (parsedJwt) { requestObjectPayload = parsedJwt.payload as RequestObjectPayload @@ -167,10 +164,7 @@ export class AuthorizationRequest { ) assertValidRPRegistrationMedataPayload(registrationMetadataPayload) // TODO: We need to do something with the metadata probably - } else { - return Promise.reject(Error(`could not fetch registrationMetadataPayload due to missing payload key ${registrationPropertyKey}`)) } - // When the response_uri parameter is present, the redirect_uri Authorization Request parameter MUST NOT be present. If the redirect_uri Authorization Request parameter is present when the Response Mode is direct_post, the Wallet MUST return an invalid_request Authorization Response error. let responseURIType: ResponseURIType let responseURI: string @@ -192,7 +186,7 @@ export class AuthorizationRequest { // TODO: we need to verify somewhere that if response_mode is direct_post, that the response_uri may be present, // BUT not both redirect_uri and response_uri. What is the best place to do this? - const presentationDefinitions: PresentationDefinitionWithLocation[] = await PresentationExchange.findValidPresentationDefinitions(mergedPayload, await this.getSupportedVersion()) + const presentationDefinitions = await PresentationExchange.findValidPresentationDefinitions(mergedPayload, await this.getSupportedVersion()) return { jwt, payload: parsedJwt?.payload, @@ -211,7 +205,7 @@ export class AuthorizationRequest { } } - static async verify(requestOrUri: string, verifyOpts: VerifyAuthorizationRequestOpts): Promise { + static async verify(requestOrUri: string, verifyOpts: VerifyAuthorizationRequestOpts) { assertValidVerifyAuthorizationRequestOpts(verifyOpts) const authorizationRequest = await AuthorizationRequest.fromUriOrJwt(requestOrUri) return await authorizationRequest.verify(verifyOpts) @@ -226,7 +220,7 @@ export class AuthorizationRequest { throw Error(SIOPErrors.BAD_PARAMS) } const requestObject = await RequestObject.fromJwt(jwt) - const payload: AuthorizationRequestPayload = { ...(requestObject && await requestObject.getPayload()) } as AuthorizationRequestPayload + const payload: AuthorizationRequestPayload = { ...(await requestObject.getPayload()) } as AuthorizationRequestPayload // Although this was a RequestObject we instantiate it as AuthzRequest and then copy in the JWT as the request Object payload.request = jwt return new AuthorizationRequest({ ...payload }, requestObject) @@ -237,23 +231,22 @@ export class AuthorizationRequest { throw Error(SIOPErrors.BAD_PARAMS) } const uriObject = typeof uri === 'string' ? await URI.fromUri(uri) : uri - const requestObject = uriObject.requestObjectJwt ? await RequestObject.fromJwt(uriObject.requestObjectJwt) : undefined + const requestObject = await RequestObject.fromJwt(uriObject.requestObjectJwt) return new AuthorizationRequest(uriObject.authorizationRequestPayload, requestObject, undefined, uriObject) } public async toStateInfo(): Promise { - - const requestObjectPayload = this.requestObject !== undefined ? await this.requestObject.getPayload() : undefined + const requestObject = await this.requestObject.getPayload() return { - client_id: this.options?.clientMetadata?.client_id ?? this.payload.client_id, - iat: requestObjectPayload?.iat ?? this.payload.iat, - nonce: requestObjectPayload?.nonce ?? this.payload.nonce, + client_id: this.options.clientMetadata.client_id, + iat: requestObject.iat ?? this.payload.iat, + nonce: requestObject.nonce ?? this.payload.nonce, state: this.payload.state, } } public async containsResponseType(singleType: ResponseType | string): Promise { - const responseType: string | undefined = await this.getMergedProperty('response_type') + const responseType: string = await this.getMergedProperty('response_type') return responseType?.includes(singleType) === true } @@ -263,14 +256,10 @@ export class AuthorizationRequest { } public async mergedPayloads(): Promise { - const requestObjectPayload = { ...this.payload, ...(this.requestObject && (await this.requestObject.getPayload())) } - if (typeof requestObjectPayload.scope !== 'string') { - throw new Error('Invalid scope value') - } - return requestObjectPayload as RequestObjectPayload + return { ...this.payload, ...(this.requestObject && (await this.requestObject.getPayload())) } } - public async getPresentationDefinitions(version?: SupportedVersion): Promise { + public async getPresentationDefinitions(version?: SupportedVersion): Promise { return await PresentationExchange.findValidPresentationDefinitions(await this.mergedPayloads(), version) } } From 8896033a67d0729fd6194db8df1cb73ef3151b95 Mon Sep 17 00:00:00 2001 From: Sadjad Khoshkhou Date: Thu, 19 Sep 2024 15:52:12 +0200 Subject: [PATCH 3/6] chore: reverted the changes in the Metadata.ts for getting the algorithms --- packages/siop-oid4vp/lib/helpers/Metadata.ts | 35 +++++--------------- 1 file changed, 8 insertions(+), 27 deletions(-) diff --git a/packages/siop-oid4vp/lib/helpers/Metadata.ts b/packages/siop-oid4vp/lib/helpers/Metadata.ts index 30368657..f1b48383 100644 --- a/packages/siop-oid4vp/lib/helpers/Metadata.ts +++ b/packages/siop-oid4vp/lib/helpers/Metadata.ts @@ -84,21 +84,6 @@ function supportedSubjectSyntaxTypes(rpMethods: string[] | string, opMethods: st return supportedSubjectSyntaxTypes } -export function collectAlgValues(o: any): string[] { - const algValues: string[] = []; - for (const key of Object.keys(o)) { - // Check if the object has an 'alg' property that's an array of strings - if (key === 'alg' && Array.isArray(o.alg)) { - algValues.push(...o.alg); - } - else if (key === 'sd-jwt_alg_values' && Array.isArray(o['sd-jwt_alg_values'])) { - algValues.push(...o['sd-jwt_alg_values']); - } - } - - return algValues; -} - function getFormatIntersection(rpFormat: Format, opFormat: Format): Format { const intersectionFormat: Record = {} const supportedCredentials = getIntersection(Object.keys(rpFormat), Object.keys(opFormat)) @@ -106,18 +91,14 @@ function getFormatIntersection(rpFormat: Format, opFormat: Format): Format { throw new Error(SIOPErrors.CREDENTIAL_FORMATS_NOT_SUPPORTED) } supportedCredentials.forEach(function (crFormat: string) { - const rpFormatElement = rpFormat[crFormat as keyof Format]; - const opFormatElement = opFormat[crFormat as keyof Format]; - const rpAlgs = collectAlgValues(rpFormatElement); - const opAlgs = collectAlgValues(opFormatElement); - let methodKeyRP = undefined; - let methodKeyOP = undefined; - if (rpFormatElement !== undefined) { - Object.keys(rpFormatElement).forEach((k) => (methodKeyRP = k)); - } - if (opFormatElement !== undefined) { - Object.keys(opFormatElement).forEach((k) => (methodKeyOP = k)); - } + const rpAlgs = [] + const opAlgs = [] + Object.keys(rpFormat[crFormat]).forEach((k) => rpAlgs.push(...rpFormat[crFormat][k])) + Object.keys(opFormat[crFormat]).forEach((k) => opAlgs.push(...opFormat[crFormat][k])) + let methodKeyRP = undefined + let methodKeyOP = undefined + Object.keys(rpFormat[crFormat]).forEach((k) => (methodKeyRP = k)) + Object.keys(opFormat[crFormat]).forEach((k) => (methodKeyOP = k)) if (methodKeyRP !== methodKeyOP) { throw new Error(SIOPErrors.CREDENTIAL_FORMATS_NOT_SUPPORTED) } From e0c592efe26742ab02d35bf39f2d450c1c51c13a Mon Sep 17 00:00:00 2001 From: Sadjad Khoshkhou Date: Thu, 19 Sep 2024 16:31:51 +0200 Subject: [PATCH 4/6] fix: fixed LanguageTagUtils to only process field names if it has a mapping --- .../siop-oid4vp/lib/helpers/LanguageTagUtils.ts | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/packages/siop-oid4vp/lib/helpers/LanguageTagUtils.ts b/packages/siop-oid4vp/lib/helpers/LanguageTagUtils.ts index c654940a..e3e800f5 100644 --- a/packages/siop-oid4vp/lib/helpers/LanguageTagUtils.ts +++ b/packages/siop-oid4vp/lib/helpers/LanguageTagUtils.ts @@ -103,16 +103,12 @@ export class LanguageTagUtils { } private static assertValidTargetFieldNames(languageTagEnabledFieldsNamesMapping: Map): void { - if (languageTagEnabledFieldsNamesMapping) { - if (!languageTagEnabledFieldsNamesMapping.size) { - throw new Error(SIOPErrors.BAD_PARAMS + ' LanguageTagEnabledFieldsNamesMapping must be non-null or non-empty') - } else { - for (const entry of languageTagEnabledFieldsNamesMapping.entries()) { - const key = entry[0] - const value = entry[1] - if (isStringNullOrEmpty(key) || isStringNullOrEmpty(value)) { - throw new Error(SIOPErrors.BAD_PARAMS + '. languageTagEnabledFieldsName must be non-null or non-empty') - } + if (languageTagEnabledFieldsNamesMapping && languageTagEnabledFieldsNamesMapping.size) { + for (const entry of languageTagEnabledFieldsNamesMapping.entries()) { + const key = entry[0] + const value = entry[1] + if (isStringNullOrEmpty(key) || isStringNullOrEmpty(value)) { + throw new Error(SIOPErrors.BAD_PARAMS + '. languageTagEnabledFieldsName must be non-null or non-empty') } } } From 2a5e3a606954745069345b1c7a3e1664b30bdded Mon Sep 17 00:00:00 2001 From: Sadjad Khoshkhou Date: Thu, 19 Sep 2024 16:43:06 +0200 Subject: [PATCH 5/6] fix: fixed LanguageTagUtils tests --- .../lib/__tests__/functions/LanguageTagUtils.spec.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/packages/siop-oid4vp/lib/__tests__/functions/LanguageTagUtils.spec.ts b/packages/siop-oid4vp/lib/__tests__/functions/LanguageTagUtils.spec.ts index 18eb5551..5fdda672 100644 --- a/packages/siop-oid4vp/lib/__tests__/functions/LanguageTagUtils.spec.ts +++ b/packages/siop-oid4vp/lib/__tests__/functions/LanguageTagUtils.spec.ts @@ -208,9 +208,10 @@ describe('Language tag util should', () => { expect(() => LanguageTagUtils.getLanguageTaggedProperties({}, null as any)).toThrowError() }) - it('throw error if list is given but not effective', async () => { + it('return empty if list is given but not effective', async () => { expect.assertions(1) - await expect(() => LanguageTagUtils.getLanguageTaggedProperties({}, [])).toThrowError() + const result = await LanguageTagUtils.getLanguageTaggedProperties({}, []) + expect(result).toEqual(new Map) }) it('throw error if list is given but no proper field names', async () => { @@ -223,9 +224,10 @@ describe('Language tag util should', () => { expect(LanguageTagUtils.getLanguageTaggedPropertiesMapped({}, null as any)).toEqual(new Map()) }) - it('throw error if mapping is given but not effective', async () => { + it('return empty map if mapping is given but not effective', async () => { expect.assertions(1) - await expect(() => LanguageTagUtils.getLanguageTaggedPropertiesMapped({}, new Map())).toThrowError() + const result = await LanguageTagUtils.getLanguageTaggedPropertiesMapped({}, new Map()) + expect(result).toEqual(new Map) }) it('throw error if mapping is given but no proper names', async () => { From 74b00cb66d8e7d7f1fc7148c8861930e8f8dbf98 Mon Sep 17 00:00:00 2001 From: Sadjad Khoshkhou Date: Thu, 19 Sep 2024 16:53:07 +0200 Subject: [PATCH 6/6] chore: reverted setting the correlationId in the VerifyAuthorizationResponseOpts of Opts --- packages/siop-oid4vp/lib/rp/Opts.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/siop-oid4vp/lib/rp/Opts.ts b/packages/siop-oid4vp/lib/rp/Opts.ts index b1a34e0e..2a4ef403 100644 --- a/packages/siop-oid4vp/lib/rp/Opts.ts +++ b/packages/siop-oid4vp/lib/rp/Opts.ts @@ -1,4 +1,4 @@ -import { defaultHasher, uuidv4 } from '@sphereon/oid4vc-common' +import { defaultHasher } from '@sphereon/oid4vc-common' import { CreateAuthorizationRequestOpts, PropertyTarget, PropertyTargets, RequestPropertyWithTargets } from '../authorization-request' import { VerifyAuthorizationResponseOpts } from '../authorization-response' @@ -48,11 +48,10 @@ export const createRequestOptsFromBuilderOrExistingOpts = (opts: { builder?: RPB return createRequestOpts } -export const createVerifyResponseOptsFromBuilderOrExistingOpts = (opts: { builder?: RPBuilder; verifyOpts?: VerifyAuthorizationResponseOpts }):VerifyAuthorizationResponseOpts => { +export const createVerifyResponseOptsFromBuilderOrExistingOpts = (opts: { builder?: RPBuilder; verifyOpts?: VerifyAuthorizationResponseOpts }): Partial => { return opts.builder ? { hasher: opts.builder.hasher ?? defaultHasher, - correlationId: uuidv4(), verifyJwtCallback: opts.builder.verifyJwtCallback, verification: { presentationVerificationCallback: opts.builder.presentationVerificationCallback,