diff --git a/packages/client/lib/AuthorizationCodeClient.ts b/packages/client/lib/AuthorizationCodeClient.ts index 9ff352ba..f247a739 100644 --- a/packages/client/lib/AuthorizationCodeClient.ts +++ b/packages/client/lib/AuthorizationCodeClient.ts @@ -5,14 +5,16 @@ import { convertJsonToURI, CreateRequestObjectMode, CredentialConfigurationSupportedV1_0_13, + CredentialDefinitionJwtVcJsonLdAndLdpVcV1_0_13, + CredentialDefinitionJwtVcJsonV1_0_13, CredentialOfferPayloadV1_0_13, CredentialOfferRequestWithBaseUrl, determineSpecVersionFromOffer, EndpointMetadataResultV1_0_13, formPost, + isW3cCredentialSupported, JsonURIMode, Jwt, - OID4VCICredentialFormat, OpenId4VCIVersion, PARMode, PKCEOpts, @@ -95,14 +97,17 @@ export const createAuthorizationRequestUrl = async ({ clientId?: string; version?: OpenId4VCIVersion; }): Promise => { - function removeDisplayAndValueTypes(obj: any): void { - for (const prop in obj) { + function removeDisplayAndValueTypes(obj: any) { + const newObj = { ...obj }; + for (const prop in newObj) { if (['display', 'value_type'].includes(prop)) { - delete obj[prop]; - } else if (typeof obj[prop] === 'object') { - removeDisplayAndValueTypes(obj[prop]); + delete newObj[prop]; + } else if (typeof newObj[prop] === 'object') { + newObj[prop] = removeDisplayAndValueTypes(newObj[prop]); } } + + return newObj; } const { redirectUri, requestObjectOpts = { requestObjectMode: CreateRequestObjectMode.NONE } } = authorizationRequest; @@ -111,7 +116,7 @@ export const createAuthorizationRequestUrl = async ({ let { scope, authorizationDetails } = authorizationRequest; const parMode = endpointMetadata?.credentialIssuerMetadata?.require_pushed_authorization_requests ? PARMode.REQUIRE - : (authorizationRequest.parMode ?? (client_id ? PARMode.AUTO : PARMode.NEVER)); + : authorizationRequest.parMode ?? (client_id ? PARMode.AUTO : PARMode.NEVER); // 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) { @@ -127,42 +132,42 @@ export const createAuthorizationRequestUrl = async ({ ? filterSupportedCredentials(credentialOffer.credential_offer as CredentialOfferPayloadV1_0_13, credentialConfigurationSupported) : []; - // FIXME: complains about VCT for sd-jwt - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore authorizationDetails = creds.flatMap((cred) => { const locations = [credentialOffer?.credential_offer.credential_issuer ?? endpointMetadata.issuer]; + + // TODO: credential_configuration_id seems to always be defined? const credential_configuration_id: string | undefined = cred.configuration_id; - const vct: string | undefined = cred.vct; - let format: OID4VCICredentialFormat | undefined; + const format = credential_configuration_id ? undefined : cred.format; - if (!credential_configuration_id) { - format = cred.format; - } if (!credential_configuration_id && !cred.format) { throw Error('format is required in authorization details'); } - const meta: any = {}; - const credential_definition = cred.credential_definition; - if (credential_definition?.type && !format) { - // ype: OPTIONAL. Array as defined in Appendix A.1.1.2. This claim contains the type values the Wallet requests authorization for at the Credential Issuer. It MUST be present if the claim format is present in the root of the authorization details object. It MUST not be present otherwise. - // It meens we have a config_id, already mapping it to an explicit format and types - delete credential_definition.type; - } - if (credential_definition.credentialSubject) { - removeDisplayAndValueTypes(credential_definition.credentialSubject); + // SD-JWT VC + const vct = cred.format === 'vc+sd-jwt' ? cred.vct : undefined; + + // W3C credentials + let credential_definition: undefined | Partial = + undefined; + if (isW3cCredentialSupported(cred)) { + credential_definition = { + ...cred.credential_definition, + // type: OPTIONAL. Array as defined in Appendix A.1.1.2. This claim contains the type values the Wallet requests authorization for at the Credential Issuer. It MUST be present if the claim format is present in the root of the authorization details object. It MUST not be present otherwise. + // It meens we have a config_id, already mapping it to an explicit format and types + type: format ? cred.credential_definition.type : undefined, + credentialSubject: cred.credential_definition.credentialSubject + ? removeDisplayAndValueTypes(cred.credential_definition.credentialSubject) + : undefined, + }; } return { type: 'openid_credential', - ...meta, locations, ...(credential_definition && { credential_definition }), ...(credential_configuration_id && { credential_configuration_id }), ...(format && { format }), - ...(vct && { vct }), - ...(cred.claims && { claims: removeDisplayAndValueTypes(JSON.parse(JSON.stringify(cred.claims))) }), + ...(vct && { vct, claims: cred.claims ? removeDisplayAndValueTypes(cred.claims) : undefined }), } as AuthorizationDetails; }); if (!authorizationDetails || authorizationDetails.length === 0) { diff --git a/packages/client/lib/AuthorizationCodeClientV1_0_11.ts b/packages/client/lib/AuthorizationCodeClientV1_0_11.ts index f5aba26a..8b17b318 100644 --- a/packages/client/lib/AuthorizationCodeClientV1_0_11.ts +++ b/packages/client/lib/AuthorizationCodeClientV1_0_11.ts @@ -40,7 +40,7 @@ export const createAuthorizationRequestUrlV1_0_11 = async ({ const parMode = endpointMetadata?.credentialIssuerMetadata?.require_pushed_authorization_requests ? PARMode.REQUIRE - : (authorizationRequest.parMode ?? PARMode.AUTO); + : authorizationRequest.parMode ?? PARMode.AUTO; // 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) { diff --git a/packages/client/lib/CredentialRequestClient.ts b/packages/client/lib/CredentialRequestClient.ts index 7959096a..421852ee 100644 --- a/packages/client/lib/CredentialRequestClient.ts +++ b/packages/client/lib/CredentialRequestClient.ts @@ -6,7 +6,6 @@ import { getUniformFormat, isDeferredCredentialResponse, isValidURL, - JsonLdIssuerCredentialDefinition, OID4VCICredentialFormat, OpenId4VCIVersion, OpenIDResponse, @@ -203,7 +202,9 @@ export class CredentialRequestClient { // TODO: we should move format specific logic if (format === 'jwt_vc_json' || format === 'jwt_vc') { return { - types, + credential_definition: { + type: types, + }, format, proof, ...opts.subjectIssuance, @@ -218,13 +219,10 @@ export class CredentialRequestClient { proof, ...opts.subjectIssuance, - // Ignored because v11 does not have the context value, but it is required in v12 - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore credential_definition: { - types, - ...(opts.context && { '@context': opts.context }), - } as JsonLdIssuerCredentialDefinition, + type: types, + '@context': opts.context as string[], + }, }; } else if (format === 'vc+sd-jwt') { if (types.length > 1) { @@ -236,7 +234,7 @@ export class CredentialRequestClient { proof, vct: types[0], ...opts.subjectIssuance, - } as CredentialRequestV1_0_13; + }; } throw new Error(`Unsupported format: ${format}`); diff --git a/packages/client/lib/OpenID4VCIClient.ts b/packages/client/lib/OpenID4VCIClient.ts index 9f14b5ef..f9400caf 100644 --- a/packages/client/lib/OpenID4VCIClient.ts +++ b/packages/client/lib/OpenID4VCIClient.ts @@ -549,7 +549,7 @@ export class OpenID4VCIClient { issuerSupportedFlowTypes(): AuthzFlowType[] { return ( this.credentialOffer?.supportedFlows ?? - ((this._state.endpointMetadata?.credentialIssuerMetadata?.authorization_endpoint ?? this._state.endpointMetadata?.authorization_server) + (this._state.endpointMetadata?.credentialIssuerMetadata?.authorization_endpoint ?? this._state.endpointMetadata?.authorization_server ? [AuthzFlowType.AUTHORIZATION_CODE_FLOW] : []) ); diff --git a/packages/client/lib/__tests__/CredentialRequestClientBuilder.spec.ts b/packages/client/lib/__tests__/CredentialRequestClientBuilder.spec.ts index 23af7e8a..71cc99e2 100644 --- a/packages/client/lib/__tests__/CredentialRequestClientBuilder.spec.ts +++ b/packages/client/lib/__tests__/CredentialRequestClientBuilder.spec.ts @@ -1,14 +1,6 @@ import { KeyObject } from 'crypto'; -import { - Alg, - CredentialIssuerMetadataV1_0_13, - CredentialRequestV1_0_13, - Jwt, - JwtVerifyResult, - OpenId4VCIVersion, - ProofOfPossession, -} from '@sphereon/oid4vci-common'; +import { Alg, CredentialIssuerMetadataV1_0_13, Jwt, JwtVerifyResult, OpenId4VCIVersion, ProofOfPossession } from '@sphereon/oid4vci-common'; import * as jose from 'jose'; import { CredentialRequestOpts, ProofOfPossessionBuilder } from '..'; @@ -112,7 +104,7 @@ describe('Credential Request Client Builder', () => { .withKid(kid) .build(); await proofOfPossessionVerifierCallbackFunction({ ...proof, kid }); - const credentialRequest: CredentialRequestV1_0_13 = await credReqClient.createCredentialRequest({ + const credentialRequest = await credReqClient.createCredentialRequest({ proofInput: proof, credentialIdentifier: 'OpenBadgeCredential', version: OpenId4VCIVersion.VER_1_0_13, @@ -142,7 +134,7 @@ describe('Credential Request Client Builder', () => { .withKid(kid_withoutDid) .build(); await proofOfPossessionVerifierCallbackFunction({ ...proof, kid: kid_withoutDid }); - const credentialRequest: CredentialRequestV1_0_13 = await credReqClient.createCredentialRequest({ + const credentialRequest = await credReqClient.createCredentialRequest({ proofInput: proof, credentialTypes: 'OpenBadgeCredential', version: OpenId4VCIVersion.VER_1_0_13, diff --git a/packages/client/lib/__tests__/SdJwt.spec.ts b/packages/client/lib/__tests__/SdJwt.spec.ts index a699988b..3d58e94a 100644 --- a/packages/client/lib/__tests__/SdJwt.spec.ts +++ b/packages/client/lib/__tests__/SdJwt.spec.ts @@ -1,7 +1,7 @@ import { AccessTokenRequest, + CredentialConfigurationSupportedSdJwtVcV1_0_13, CredentialConfigurationSupportedV1_0_13, - CredentialRequestV1_0_13, CredentialSupportedSdJwtVc, } from '@sphereon/oid4vci-common'; // eslint-disable-next-line @typescript-eslint/ban-ts-comment @@ -109,7 +109,7 @@ describe('sd-jwt vc', () => { const supported = client.getCredentialsSupported('vc+sd-jwt'); expect(supported).toEqual({ SdJwtCredentialId: { format: 'vc+sd-jwt', id: 'SdJwtCredentialId', vct: 'SdJwtCredentialId' } }); - const offered = supported['SdJwtCredentialId'] as CredentialSupportedSdJwtVc; + const offered = supported['SdJwtCredentialId'] as CredentialConfigurationSupportedSdJwtVcV1_0_13; nock(issuerMetadata.token_endpoint as string) .post('/') @@ -130,7 +130,7 @@ describe('sd-jwt vc', () => { .post('/') .reply(200, async (_, body) => vcIssuer.issueCredential({ - credentialRequest: { ...(body as CredentialRequestV1_0_13), credential_identifier: offered.vct }, + credentialRequest: { ...(body as any), credential_identifier: 'SdJwtCredentialId' }, credential: { vct: 'Hello', iss: 'did:example:123', @@ -233,7 +233,7 @@ describe('sd-jwt vc', () => { .post('/') .reply(200, async (_, body) => vcIssuer.issueCredential({ - credentialRequest: { ...(body as CredentialRequestV1_0_13), credential_identifier: offered.vct }, + credentialRequest: { ...(body as any), credential_identifier: offered.vct }, credential: { vct: 'Hello', iss: 'example.com', diff --git a/packages/common/lib/functions/CredentialRequestUtil.ts b/packages/common/lib/functions/CredentialRequestUtil.ts index 3ee7891f..4608a68c 100644 --- a/packages/common/lib/functions/CredentialRequestUtil.ts +++ b/packages/common/lib/functions/CredentialRequestUtil.ts @@ -11,21 +11,31 @@ import { getFormatForVersion } from './FormatUtils'; export function getTypesFromRequest(credentialRequest: CredentialRequest, opts?: { filterVerifiableCredential: boolean }) { let types: string[] = []; + console.log(credentialRequest); if ('credential_identifier' in credentialRequest && credentialRequest.credential_identifier) { throw Error(`Cannot get types from request when it contains a credential_identifier`); - } else if (credentialRequest.format === 'jwt_vc_json' || credentialRequest.format === 'jwt_vc') { - types = 'types' in credentialRequest ? credentialRequest.types : []; - } else if (credentialRequest.format === 'jwt_vc_json-ld' || credentialRequest.format === 'ldp_vc') { - types = - 'credential_definition' in credentialRequest && credentialRequest.credential_definition - ? credentialRequest.credential_definition.types - : // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - 'types' in credentialRequest.types - ? (credentialRequest['types' as keyof CredentialRequest] as unknown as string[]) - : []; - } else if (credentialRequest.format === 'vc+sd-jwt') { - types = 'vct' in credentialRequest ? [credentialRequest.vct as string] : []; + } else if ( + credentialRequest.format === 'jwt_vc_json-ld' || + credentialRequest.format === 'ldp_vc' || + credentialRequest.format === 'jwt_vc' || + credentialRequest.format === 'jwt_vc_json' + ) { + if ('credential_definition' in credentialRequest && credentialRequest.credential_definition) { + types = + 'types' in credentialRequest.credential_definition + ? credentialRequest.credential_definition.types + : credentialRequest.credential_definition.type; + } + + if ('type' in credentialRequest && Array.isArray(credentialRequest.type)) { + types = credentialRequest.type; + } + + if ('types' in credentialRequest && Array.isArray(credentialRequest.types)) { + types = credentialRequest.types; + } + } else if (credentialRequest.format === 'vc+sd-jwt' && 'vct' in credentialRequest) { + types = [credentialRequest.vct]; } if (!types || types.length === 0) { diff --git a/packages/common/lib/functions/IssuerMetadataUtils.ts b/packages/common/lib/functions/IssuerMetadataUtils.ts index 005bd237..e068ff81 100644 --- a/packages/common/lib/functions/IssuerMetadataUtils.ts +++ b/packages/common/lib/functions/IssuerMetadataUtils.ts @@ -1,4 +1,4 @@ -import { getTypesFromObject, VCI_LOG_COMMON } from '../index'; +import { getTypesFromObject, isW3cCredentialSupported, VCI_LOG_COMMON } from '../index'; import { AuthorizationServerMetadata, CredentialConfigurationSupported, @@ -112,13 +112,11 @@ export function getSupportedCredential(opts?: { } else if (types) { isTypeMatch = normalizedTypes.every((type) => types.includes(type)); } else { - if ('credential_definition' in config) { - isTypeMatch = normalizedTypes.every((type) => config.credential_definition.type?.includes(type)); - } else if ('type' in config && Array.isArray(config.type)) { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - isTypeMatch = normalizedTypes.every((type) => config.type.includes(type)); - } else if ('types' in config) { + if (isW3cCredentialSupported(config) && 'credential_definition' in config) { + isTypeMatch = normalizedTypes.every((type) => config.credential_definition.type.includes(type)); + } else if (isW3cCredentialSupported(config) && 'type' in config && Array.isArray(config.type)) { + isTypeMatch = normalizedTypes.every((type) => (config.type as string[]).includes(type)); + } else if (isW3cCredentialSupported(config) && 'types' in config) { isTypeMatch = normalizedTypes.every((type) => config.types?.includes(type)); } } @@ -183,7 +181,7 @@ export function getIssuerDisplays(metadata: CredentialIssuerMetadata | IssuerMet metadata.display?.filter( (item) => !opts?.prefLocales || opts.prefLocales.length === 0 || (item.locale && opts.prefLocales.includes(item.locale)) || !item.locale, ) ?? []; - return matchedDisplays.sort((item) => (item.locale ? (opts?.prefLocales.indexOf(item.locale) ?? 1) : Number.MAX_VALUE)); + return matchedDisplays.sort((item) => (item.locale ? opts?.prefLocales.indexOf(item.locale) ?? 1 : Number.MAX_VALUE)); } /** diff --git a/packages/common/lib/functions/TypeConversionUtils.ts b/packages/common/lib/functions/TypeConversionUtils.ts index 9ca77ceb..8914abb1 100644 --- a/packages/common/lib/functions/TypeConversionUtils.ts +++ b/packages/common/lib/functions/TypeConversionUtils.ts @@ -1,5 +1,20 @@ import { AuthorizationDetails, CredentialOfferPayload, UniformCredentialOfferPayload, UniformCredentialOfferRequest, VCI_LOG_COMMON } from '../index'; -import { CredentialConfigurationSupported, CredentialDefinitionV1_0_13, CredentialOfferFormat, JsonLdIssuerCredentialDefinition } from '../types'; +import { + CredentialConfigurationSupported, + CredentialConfigurationSupportedSdJwtVcV1_0_13, + CredentialDefinitionJwtVcJsonLdAndLdpVcV1_0_13, + CredentialDefinitionJwtVcJsonV1_0_13, + CredentialOfferFormat, + CredentialsSupportedLegacy, + CredentialSupportedSdJwtVc, + JsonLdIssuerCredentialDefinition, +} from '../types'; + +export function isW3cCredentialSupported( + supported: CredentialConfigurationSupported | CredentialsSupportedLegacy, +): supported is Exclude { + return ['jwt_vc_json', 'jwt_vc_json-ld', 'ldp_vc', 'jwt_vc'].includes(supported.format); +} export const getNumberOrUndefined = (input?: string): number | undefined => { return input && !isNaN(+input) ? +input : undefined; @@ -10,13 +25,19 @@ export const getNumberOrUndefined = (input?: string): number | undefined => { * @param subject */ export function getTypesFromObject( - subject: CredentialConfigurationSupported | CredentialOfferFormat | CredentialDefinitionV1_0_13 | JsonLdIssuerCredentialDefinition | string, + subject: + | CredentialConfigurationSupported + | CredentialOfferFormat + | CredentialDefinitionJwtVcJsonLdAndLdpVcV1_0_13 + | CredentialDefinitionJwtVcJsonV1_0_13 + | JsonLdIssuerCredentialDefinition + | string, ): string[] | undefined { if (subject === undefined) { return undefined; } else if (typeof subject === 'string') { return [subject]; - } else if ('credential_definition' in subject && subject.credential_definition) { + } else if ('format' in subject && subject.format !== 'vc+sd-jwt' && 'credential_definition' in subject) { return getTypesFromObject(subject.credential_definition); } else if ('types' in subject && subject.types) { return Array.isArray(subject.types) ? subject.types : [subject.types]; @@ -77,8 +98,6 @@ export function getTypesFromCredentialSupported( ) { types = getTypesFromObject(credentialSupported) ?? []; } else if (credentialSupported.format === 'vc+sd-jwt') { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore types = [credentialSupported.vct]; } diff --git a/packages/common/lib/types/Generic.types.ts b/packages/common/lib/types/Generic.types.ts index 15071c44..0a9e4e40 100644 --- a/packages/common/lib/types/Generic.types.ts +++ b/packages/common/lib/types/Generic.types.ts @@ -123,13 +123,17 @@ export interface ProofType { proof_signing_alg_values_supported: string[]; } +export type ProofTypesSupported = { + [key in KeyProofType]?: ProofType; +}; + export type CommonCredentialSupported = CredentialSupportedBrief & ExperimentalSubjectIssuance & { format: OID4VCICredentialFormat | string; //REQUIRED. A JSON string identifying the format of this credential, e.g. jwt_vc_json or ldp_vc. id?: string; // OPTIONAL. A JSON string identifying the respective object. The value MUST be unique across all credentials_supported entries in the Credential Issuer Metadata display?: CredentialsSupportedDisplay[]; // OPTIONAL. An array of objects, where each object contains the display properties of the supported credential for a certain language scope?: string; // OPTIONAL. A JSON string identifying the scope value that this Credential Issuer supports for this particular Credential. The value can be the same across multiple credential_configurations_supported objects. The Authorization Server MUST be able to uniquely identify the Credential Issuer based on the scope value. The Wallet can use this value in the Authorization Request as defined in Section 5.1.2. Scope values in this Credential Issuer metadata MAY duplicate those in the scopes_supported parameter of the Authorization Server. - proof_types_supported?: Record; + proof_types_supported?: ProofTypesSupported; /** * following properties are non-mso_mdoc specific and we might wanna rethink them when we're going to support mso_mdoc diff --git a/packages/common/lib/types/v1_0_11.types.ts b/packages/common/lib/types/v1_0_11.types.ts index 5f609b8a..b3ef8726 100644 --- a/packages/common/lib/types/v1_0_11.types.ts +++ b/packages/common/lib/types/v1_0_11.types.ts @@ -1,5 +1,3 @@ -import { ExperimentalSubjectIssuance } from '../experimental/holder-vci'; - import { AuthorizationDetailsJwtVcJson, AuthorizationServerOpts, CommonAuthorizationRequest } from './Authorization.types'; import { UniformCredentialOffer, UniformCredentialOfferRequest } from './CredentialIssuance.types'; import { @@ -82,7 +80,6 @@ export interface CredentialOfferPayloadV1_0_11 { } export type CredentialRequestV1_0_11 = CommonCredentialRequest & - ExperimentalSubjectIssuance & (CredentialRequestJwtVcJson | CredentialRequestJwtVcJsonLdAndLdpVc | CredentialRequestSdJwtVc); export interface CredentialIssuerMetadataV1_0_11 extends CredentialIssuerMetadataOpts, Partial { diff --git a/packages/common/lib/types/v1_0_13.types.ts b/packages/common/lib/types/v1_0_13.types.ts index 873a32a5..8d1908ca 100644 --- a/packages/common/lib/types/v1_0_13.types.ts +++ b/packages/common/lib/types/v1_0_13.types.ts @@ -3,19 +3,17 @@ import { ExperimentalSubjectIssuance } from '../experimental/holder-vci'; import { JWK, ProofOfPossession } from './CredentialIssuance.types'; import { AlgValue, + CommonCredentialRequest, CredentialDataSupplierInput, - CredentialRequestJwtVcJson, - CredentialRequestJwtVcJsonLdAndLdpVc, CredentialRequestSdJwtVc, CredentialsSupportedDisplay, CredentialSupplierConfig, EncValue, Grant, IssuerCredentialSubject, - KeyProofType, MetadataDisplay, OID4VCICredentialFormat, - ProofType, + ProofTypesSupported, ResponseEncryption, } from './Generic.types'; import { QRCodeOpts } from './QRCode.types'; @@ -37,42 +35,90 @@ export interface IssuerMetadataV1_0_13 { [x: string]: unknown; } -export type CredentialDefinitionV1_0_13 = { - type?: string[]; +export type CredentialDefinitionJwtVcJsonV1_0_13 = { + type: string[]; credentialSubject?: IssuerCredentialSubject; }; -export type CredentialConfigurationSupportedV1_0_13 = { - credential_definition: CredentialDefinitionV1_0_13; - /** - * TODO: These two (vct and id) are solely added because of backward compatibility with sd-jwt. as soons as we have a clear understanding of the new sd-jwt issuer protocol we can remove this - */ - vct?: string; - id?: string; - claims?: IssuerCredentialSubject; - format: OID4VCICredentialFormat; //REQUIRED. A JSON string identifying the format of this credential, e.g. jwt_vc_json or ldp_vc. +export type CredentialDefinitionJwtVcJsonLdAndLdpVcV1_0_13 = { + '@context': string[]; + type: string[]; + credentialSubject?: IssuerCredentialSubject; +}; + +export type CredentialConfigurationSupportedV1_0_13 = CredentialConfigurationSupportedCommonV1_0_13 & + ( + | CredentialConfigurationSupportedSdJwtVcV1_0_13 + | CredentialConfigurationSupportedJwtVcJsonV1_0_13 + | CredentialConfigurationSupportedJwtVcJsonLdAndLdpVcV1_0_13 + ); + +// Base type covering credential configurations supported +export type CredentialConfigurationSupportedCommonV1_0_13 = { + format: OID4VCICredentialFormat | 'string'; //REQUIRED. A JSON string identifying the format of this credential, e.g. jwt_vc_json or ldp_vc. scope?: string; // OPTIONAL. A JSON string identifying the scope value that this Credential Issuer supports for this particular Credential. The value can be the same across multiple credential_configurations_supported objects. The Authorization Server MUST be able to uniquely identify the Credential Issuer based on the scope value. The Wallet can use this value in the Authorization Request as defined in Section 5.1.2. Scope values in this Credential Issuer metadata MAY duplicate those in the scopes_supported parameter of the Authorization Server. cryptographic_binding_methods_supported?: string[]; credential_signing_alg_values_supported?: string[]; - proof_types_supported?: Record; + proof_types_supported?: ProofTypesSupported; display?: CredentialsSupportedDisplay[]; // OPTIONAL. An array of objects, where each object contains the display properties of the supported credential for a certain language [x: string]: unknown; }; -export type CredentialRequestV1_0_13 = - | (ExperimentalSubjectIssuance & { - // We add them here to keep existing other request versions happy. They cannot co-exists with an identifier - format?: OID4VCICredentialFormat /* | OID4VCICredentialFormat[];*/; // for now it seems only one is supported in the spec - proof?: ProofOfPossession; - - credential_identifier?: string; - credential_response_encryption?: { - jwk: JWK; - alg: AlgValue; - enc: EncValue; - }; - }) - | (CredentialRequestJwtVcJson | CredentialRequestJwtVcJsonLdAndLdpVc | CredentialRequestSdJwtVc); +export interface CredentialConfigurationSupportedSdJwtVcV1_0_13 extends CredentialConfigurationSupportedCommonV1_0_13 { + format: 'vc+sd-jwt'; + + vct: string; + claims?: IssuerCredentialSubject; + + order?: string[]; //An array of claims.display.name values that lists them in the order they should be displayed by the Wallet. +} + +export interface CredentialConfigurationSupportedJwtVcJsonV1_0_13 extends CredentialConfigurationSupportedCommonV1_0_13 { + format: 'jwt_vc_json' | 'jwt_vc'; + credential_definition: CredentialDefinitionJwtVcJsonV1_0_13; + order?: string[]; //An array of claims.display.name values that lists them in the order they should be displayed by the Wallet. +} + +export interface CredentialConfigurationSupportedJwtVcJsonLdAndLdpVcV1_0_13 extends CredentialConfigurationSupportedCommonV1_0_13 { + format: 'ldp_vc' | 'jwt_vc_json-ld'; + credential_definition: CredentialDefinitionJwtVcJsonLdAndLdpVcV1_0_13; + order?: string[]; //An array of claims.display.name values that lists them in the order they should be displayed by the Wallet. +} + +export type CredentialRequestV1_0_13ResponseEncryption = { + jwk: JWK; + alg: AlgValue; + enc: EncValue; +}; + +export interface CredentialRequestV1_0_13Common extends ExperimentalSubjectIssuance { + credential_response_encryption?: CredentialRequestV1_0_13ResponseEncryption; + proof?: ProofOfPossession; +} + +export type CredentialRequestV1_0_13 = CredentialRequestV1_0_13Common & + ( + | CredentialRequestJwtVcJsonV1_0_13 + | CredentialRequestJwtVcJsonLdAndLdpVcV1_0_13 + | CredentialRequestSdJwtVc + | CredentialRequestV1_0_13CredentialIdentifier + ); + +export interface CredentialRequestV1_0_13CredentialIdentifier extends CredentialRequestV1_0_13Common { + // Format cannot be defined when credential_identifier is used + format?: undefined; + credential_identifier: string; +} + +export interface CredentialRequestJwtVcJsonV1_0_13 extends CommonCredentialRequest { + format: 'jwt_vc_json' | 'jwt_vc'; // jwt_vc for backwards compat + credential_definition: CredentialDefinitionJwtVcJsonV1_0_13; +} + +export interface CredentialRequestJwtVcJsonLdAndLdpVcV1_0_13 extends CommonCredentialRequest { + format: 'ldp_vc' | 'jwt_vc_json-ld'; + credential_definition: CredentialDefinitionJwtVcJsonLdAndLdpVcV1_0_13; +} export interface CredentialOfferV1_0_13 { credential_offer?: CredentialOfferPayloadV1_0_13; diff --git a/packages/did-auth-siop-adapter/README.md b/packages/did-auth-siop-adapter/README.md index 3549c37f..b4f037c9 100644 --- a/packages/did-auth-siop-adapter/README.md +++ b/packages/did-auth-siop-adapter/README.md @@ -8,6 +8,5 @@ with OpenID4VP support DID-JWT adapter
- A simple utility library for creating and veryfing did-jwt's with. [@spheren/did-auth-siop](https://img.shields.io/npm/v/@sphereon/did-auth-siop.svg) diff --git a/packages/issuer/lib/__tests__/VcIssuer.spec.ts b/packages/issuer/lib/__tests__/VcIssuer.spec.ts index 38dd08fa..75a3a685 100644 --- a/packages/issuer/lib/__tests__/VcIssuer.spec.ts +++ b/packages/issuer/lib/__tests__/VcIssuer.spec.ts @@ -316,7 +316,6 @@ describe('VcIssuer', () => { vcIssuer.issueCredential({ credentialRequest: { credential_identifier: 'VerifiableCredential', - format: 'jwt_vc_json', proof: { proof_type: 'jwt', jwt: 'ye.ye.ye', @@ -375,7 +374,6 @@ describe('VcIssuer', () => { credential: verifiableCredential, credentialRequest: { credential_identifier: 'VerifiableCredential', - format: 'jwt_vc_json', proof: { proof_type: 'jwt', jwt: 'ye.ye.ye', @@ -439,7 +437,6 @@ describe('VcIssuer', () => { vcIssuer.issueCredential({ credentialRequest: { credential_identifier: 'VerifiableCredential', - format: 'jwt_vc_json', proof: { proof_type: 'jwt', jwt: 'ye.ye.ye', @@ -578,7 +575,6 @@ describe('VcIssuer without did', () => { vcIssuer.issueCredential({ credentialRequest: { credential_identifier: 'VerifiableCredential', - format: 'jwt_vc_json', proof: { proof_type: 'jwt', jwt: 'ye.ye.ye', @@ -631,7 +627,6 @@ describe('VcIssuer without did', () => { credential: verifiableCredential_withoutDid, credentialRequest: { credential_identifier: 'VerifiableCredential', - format: 'jwt_vc_json', proof: { proof_type: 'jwt', jwt: 'ye.ye.ye', @@ -689,7 +684,6 @@ describe('VcIssuer without did', () => { vcIssuer.issueCredential({ credentialRequest: { credential_identifier: 'VerifiableCredential', - format: 'jwt_vc_json', proof: { proof_type: 'jwt', jwt: 'ye.ye.ye', diff --git a/packages/issuer/lib/builder/CredentialSupportedBuilderV1_13.ts b/packages/issuer/lib/builder/CredentialSupportedBuilderV1_13.ts index cbf732d7..0a4d64bf 100644 --- a/packages/issuer/lib/builder/CredentialSupportedBuilderV1_13.ts +++ b/packages/issuer/lib/builder/CredentialSupportedBuilderV1_13.ts @@ -1,12 +1,14 @@ import { CredentialConfigurationSupportedV1_0_13, - CredentialDefinitionV1_0_13, + CredentialDefinitionJwtVcJsonLdAndLdpVcV1_0_13, + CredentialDefinitionJwtVcJsonV1_0_13, CredentialsSupportedDisplay, IssuerCredentialSubject, IssuerCredentialSubjectDisplay, KeyProofType, OID4VCICredentialFormat, ProofType, + ProofTypesSupported, TokenErrorResponse, } from '@sphereon/oid4vci-common' @@ -14,10 +16,10 @@ export class CredentialSupportedBuilderV1_13 { format?: OID4VCICredentialFormat scope?: string credentialName?: string - credentialDefinition?: CredentialDefinitionV1_0_13 + credentialDefinition?: CredentialDefinitionJwtVcJsonLdAndLdpVcV1_0_13 | CredentialDefinitionJwtVcJsonV1_0_13 cryptographicBindingMethodsSupported?: ('jwk' | 'cose_key' | 'did' | string)[] credentialSigningAlgValuesSupported?: string[] - proofTypesSupported?: Record + proofTypesSupported?: ProofTypesSupported display?: CredentialsSupportedDisplay[] credentialSubject?: IssuerCredentialSubject @@ -31,7 +33,9 @@ export class CredentialSupportedBuilderV1_13 { return this } - withCredentialDefinition(credentialDefinition: CredentialDefinitionV1_0_13): CredentialSupportedBuilderV1_13 { + withCredentialDefinition( + credentialDefinition: CredentialDefinitionJwtVcJsonLdAndLdpVcV1_0_13 | CredentialDefinitionJwtVcJsonV1_0_13, + ): CredentialSupportedBuilderV1_13 { if (!credentialDefinition.type) { throw new Error('credentialDefinition should contain a type array') } @@ -81,13 +85,13 @@ export class CredentialSupportedBuilderV1_13 { addProofTypesSupported(keyProofType: KeyProofType, proofType: ProofType): CredentialSupportedBuilderV1_13 { if (!this.proofTypesSupported) { - this.proofTypesSupported = {} as Record + this.proofTypesSupported = {} } this.proofTypesSupported[keyProofType] = proofType return this } - withProofTypesSupported(proofTypesSupported: Record): CredentialSupportedBuilderV1_13 { + withProofTypesSupported(proofTypesSupported: ProofTypesSupported): CredentialSupportedBuilderV1_13 { this.proofTypesSupported = proofTypesSupported return this } diff --git a/packages/siop-oid4vp/lib/__tests__/DidJwtTestUtils.ts b/packages/siop-oid4vp/lib/__tests__/DidJwtTestUtils.ts index 4d40dc26..c034c439 100644 --- a/packages/siop-oid4vp/lib/__tests__/DidJwtTestUtils.ts +++ b/packages/siop-oid4vp/lib/__tests__/DidJwtTestUtils.ts @@ -113,9 +113,9 @@ export function getVerifyJwtCallback( resolver = resolver ?? getResolver(['ethr', 'ion']) const audience = jwtVerifier.type === 'request-object' - ? (verifyOpts?.audience ?? getAudience(jwt.raw)) + ? verifyOpts?.audience ?? getAudience(jwt.raw) : jwtVerifier.type === 'id-token' - ? (verifyOpts?.audience ?? getAudience(jwt.raw)) + ? verifyOpts?.audience ?? getAudience(jwt.raw) : undefined await verifyDidJWT(jwt.raw, resolver, { audience, ...verifyOpts })