diff --git a/.changeset/fresh-coins-punch.md b/.changeset/fresh-coins-punch.md new file mode 100644 index 00000000..8adb772a --- /dev/null +++ b/.changeset/fresh-coins-punch.md @@ -0,0 +1,5 @@ +--- +"@openid4vc/openid4vci": minor +--- + +feat: update draft 16 to v1.0. This only introduces one breaking change requiring a transaction_id in the deferred credential response. For this reason we replaced the Draft16 support with V1. All other drafts are still supported. diff --git a/packages/oauth2/src/access-token/parse-access-token-request.ts b/packages/oauth2/src/access-token/parse-access-token-request.ts index b54ad3c2..4b844613 100644 --- a/packages/oauth2/src/access-token/parse-access-token-request.ts +++ b/packages/oauth2/src/access-token/parse-access-token-request.ts @@ -12,7 +12,11 @@ import { type RefreshTokenGrantIdentifier, refreshTokenGrantIdentifier, } from '../z-grant-type' -import { type AccessTokenRequest, zAccessTokenRequest } from './z-access-token' +import { + type AccessTokenRequest, + zAccessTokenRequest, + zAccessTokenRequestParsedUriParamsToJson, +} from './z-access-token' export interface ParsedAccessTokenPreAuthorizedCodeRequestGrant { grantType: PreAuthorizedCodeGrantIdentifier @@ -78,7 +82,10 @@ export interface ParseAccessTokenRequestOptions { * that can be returned to the client. */ export function parseAccessTokenRequest(options: ParseAccessTokenRequestOptions): ParseAccessTokenRequestResult { - const parsedAccessTokenRequest = zAccessTokenRequest.safeParse(options.accessTokenRequest) + const parsedAccessTokenRequest = zAccessTokenRequestParsedUriParamsToJson + .pipe(zAccessTokenRequest) + .safeParse(options.accessTokenRequest) + if (!parsedAccessTokenRequest.success) { throw new Oauth2ServerErrorResponseError({ error: Oauth2ErrorCodes.InvalidRequest, diff --git a/packages/oauth2/src/access-token/z-access-token-jwt.ts b/packages/oauth2/src/access-token/z-access-token-jwt.ts index 8af64452..aef24a22 100644 --- a/packages/oauth2/src/access-token/z-access-token-jwt.ts +++ b/packages/oauth2/src/access-token/z-access-token-jwt.ts @@ -1,6 +1,7 @@ import { zInteger } from '@openid4vc/utils' import z from 'zod' import { zJwtHeader, zJwtPayload } from '../common/jwt/z-jwt' +import { zAuthorizationDetailsEntryBase } from '../common/z-authorization-details' export const zAccessTokenProfileJwtHeader = z .object({ @@ -25,6 +26,10 @@ export const zAccessTokenProfileJwtPayload = z // SHOULD be included in the authorization request contained it scope: z.optional(z.string()), + + // Authorization Details may be added to jwt access token + // to allow a resource server to understand the authorizations + authorization_details: z.array(zAuthorizationDetailsEntryBase).optional(), }) .loose() diff --git a/packages/oauth2/src/access-token/z-access-token.ts b/packages/oauth2/src/access-token/z-access-token.ts index b761d84b..0b381346 100644 --- a/packages/oauth2/src/access-token/z-access-token.ts +++ b/packages/oauth2/src/access-token/z-access-token.ts @@ -1,5 +1,6 @@ -import { zHttpsUrl } from '@openid4vc/utils' +import { zHttpsUrl, zStringToJson } from '@openid4vc/utils' import z from 'zod' +import { zAuthorizationDetailsEntryBase } from '../common/z-authorization-details' import { zOauth2ErrorResponse } from '../common/z-oauth2-error' import { zAuthorizationCodeGrantIdentifier, @@ -30,6 +31,8 @@ export const zAccessTokenRequest = z.intersection( // string makes the previous ones unnecessary, but it does help with error messages z.string(), ]), + + authorization_details: z.array(zAuthorizationDetailsEntryBase).optional(), }) .loose(), z @@ -51,6 +54,11 @@ export const zAccessTokenRequest = z.intersection( ) export type AccessTokenRequest = z.infer +// We need to parse serialized JSON to an JSON object. +export const zAccessTokenRequestParsedUriParamsToJson = z.looseObject({ + authorization_details: zStringToJson.optional(), +}) + export const zAccessTokenResponse = z .object({ access_token: z.string(), @@ -66,17 +74,7 @@ export const zAccessTokenResponse = z c_nonce: z.optional(z.string()), c_nonce_expires_in: z.optional(z.number().int()), - // TODO: add additional params - authorization_details: z - .array( - z - .object({ - // required when type is openid_credential (so we probably need a discriminator) - // credential_identifiers: z.array(z.string()), - }) - .loose() - ) - .optional(), + authorization_details: z.array(zAuthorizationDetailsEntryBase).optional(), }) .loose() diff --git a/packages/oauth2/src/access-token/z-token-introspection.ts b/packages/oauth2/src/access-token/z-token-introspection.ts index 004ba37b..ce691525 100644 --- a/packages/oauth2/src/access-token/z-token-introspection.ts +++ b/packages/oauth2/src/access-token/z-token-introspection.ts @@ -1,6 +1,7 @@ import { zInteger } from '@openid4vc/utils' import z from 'zod' import { zJwtConfirmationPayload } from '../common/jwt/z-jwt' +import { zAuthorizationDetailsEntryBase } from '../common/z-authorization-details' export const zTokenIntrospectionRequest = z .object({ @@ -30,6 +31,10 @@ export const zTokenIntrospectionResponse = z jti: z.optional(z.string()), cnf: z.optional(zJwtConfirmationPayload), + + // Authorization Details may be added to introspection response + // to allow a resource server to understand the authorizations + authorization_details: z.array(zAuthorizationDetailsEntryBase).optional(), }) .loose() diff --git a/packages/oauth2/src/authorization-challenge/parse-authorization-challenge-request.ts b/packages/oauth2/src/authorization-challenge/parse-authorization-challenge-request.ts index ab903390..31aae7b6 100644 --- a/packages/oauth2/src/authorization-challenge/parse-authorization-challenge-request.ts +++ b/packages/oauth2/src/authorization-challenge/parse-authorization-challenge-request.ts @@ -3,6 +3,7 @@ import { type ParseAuthorizationRequestResult, parseAuthorizationRequest, } from '../authorization-request/parse-authorization-request' +import { zAuthorizationRequestParsedUriParamsToJson } from '../authorization-request/z-authorization-request' import type { RequestLike } from '../common/z-common' import { Oauth2ErrorCodes } from '../common/z-oauth2-error' import { Oauth2ServerErrorResponseError } from '../error/Oauth2ServerErrorResponseError' @@ -26,9 +27,11 @@ export interface ParseAuthorizationChallengeRequestResult extends ParseAuthoriza export function parseAuthorizationChallengeRequest( options: ParseAuthorizationChallengeRequestOptions ): ParseAuthorizationChallengeRequestResult { - const parsedAuthorizationChallengeRequest = zAuthorizationChallengeRequest.safeParse( - options.authorizationChallengeRequest - ) + // First ensure we correctly transform the serialized entries to JSON + const parsedAuthorizationChallengeRequest = zAuthorizationRequestParsedUriParamsToJson + .pipe(zAuthorizationChallengeRequest) + .safeParse(options.authorizationChallengeRequest) + if (!parsedAuthorizationChallengeRequest.success) { throw new Oauth2ServerErrorResponseError({ error: Oauth2ErrorCodes.InvalidRequest, diff --git a/packages/oauth2/src/authorization-request/parse-pushed-authorization-request.ts b/packages/oauth2/src/authorization-request/parse-pushed-authorization-request.ts index 4086868c..f5be409d 100644 --- a/packages/oauth2/src/authorization-request/parse-pushed-authorization-request.ts +++ b/packages/oauth2/src/authorization-request/parse-pushed-authorization-request.ts @@ -3,7 +3,11 @@ import type { RequestLike } from '../common/z-common' import { Oauth2ErrorCodes } from '../common/z-oauth2-error' import { Oauth2ServerErrorResponseError } from '../error/Oauth2ServerErrorResponseError' import { type ParseAuthorizationRequestResult, parseAuthorizationRequest } from './parse-authorization-request' -import { type AuthorizationRequest, zAuthorizationRequest } from './z-authorization-request' +import { + type AuthorizationRequest, + zAuthorizationRequest, + zAuthorizationRequestParsedUriParamsToJson, +} from './z-authorization-request' export interface ParsePushedAuthorizationRequestOptions { request: RequestLike @@ -21,7 +25,10 @@ export interface ParsePushedAuthorizationRequestResult extends ParseAuthorizatio export function parsePushedAuthorizationRequest( options: ParsePushedAuthorizationRequestOptions ): ParsePushedAuthorizationRequestResult { - const parsedAuthorizationRequest = zAuthorizationRequest.safeParse(options.authorizationRequest) + const parsedAuthorizationRequest = zAuthorizationRequestParsedUriParamsToJson + .pipe(zAuthorizationRequest) + .safeParse(options.authorizationRequest) + if (!parsedAuthorizationRequest.success) { throw new Oauth2ServerErrorResponseError({ error: Oauth2ErrorCodes.InvalidRequest, diff --git a/packages/oauth2/src/authorization-request/z-authorization-request.ts b/packages/oauth2/src/authorization-request/z-authorization-request.ts index 4c4cdf53..b1e8fbd6 100644 --- a/packages/oauth2/src/authorization-request/z-authorization-request.ts +++ b/packages/oauth2/src/authorization-request/z-authorization-request.ts @@ -1,5 +1,6 @@ -import { zHttpsUrl } from '@openid4vc/utils' +import { zHttpsUrl, zStringToJson } from '@openid4vc/utils' import z from 'zod' +import { zAuthorizationDetailsEntryBase } from '../common/z-authorization-details' import { zOauth2ErrorResponse } from '../common/z-oauth2-error' // TODO: should create different request validations for different @@ -13,6 +14,7 @@ export const zAuthorizationRequest = z redirect_uri: z.url().optional(), resource: z.optional(zHttpsUrl), scope: z.optional(z.string()), + authorization_details: z.array(zAuthorizationDetailsEntryBase).optional(), // DPoP jwk thumbprint dpop_jkt: z.optional(z.base64url()), @@ -23,6 +25,11 @@ export const zAuthorizationRequest = z .loose() export type AuthorizationRequest = z.infer +// We need to parse serialized JSON to an JSON object. +export const zAuthorizationRequestParsedUriParamsToJson = z.looseObject({ + authorization_details: zStringToJson.optional(), +}) + export const zPushedAuthorizationRequest = z .object({ request_uri: z.string(), diff --git a/packages/oauth2/src/common/z-authorization-details.ts b/packages/oauth2/src/common/z-authorization-details.ts new file mode 100644 index 00000000..bf4c0236 --- /dev/null +++ b/packages/oauth2/src/common/z-authorization-details.ts @@ -0,0 +1,11 @@ +import z from 'zod' + +export const zAuthorizationDetailsEntryBase = z.object({ + type: z.string(), + + locations: z.array(z.string()).optional(), + actions: z.array(z.string()).optional(), + datatypes: z.array(z.string()).optional(), + identifier: z.string().optional(), + privileges: z.array(z.string()).optional(), +}) diff --git a/packages/oauth2/src/common/z-oauth2-error.ts b/packages/oauth2/src/common/z-oauth2-error.ts index cd3306f9..24eceb2b 100644 --- a/packages/oauth2/src/common/z-oauth2-error.ts +++ b/packages/oauth2/src/common/z-oauth2-error.ts @@ -52,6 +52,9 @@ export enum Oauth2ErrorCodes { InvalidRequestUriMethod = 'invalid_request_uri_method', InvalidTransactionData = 'invalid_transaction_data', WalletUnavailable = 'wallet_unavailable', + + // Rich Authorization Requests + InvalidAuthorizationDetails = 'invalid_authorization_details', } export const zOauth2ErrorResponse = z diff --git a/packages/oauth2/src/metadata/authorization-server/z-authorization-server-metadata.ts b/packages/oauth2/src/metadata/authorization-server/z-authorization-server-metadata.ts index 89d864b4..cdb5cf0c 100644 --- a/packages/oauth2/src/metadata/authorization-server/z-authorization-server-metadata.ts +++ b/packages/oauth2/src/metadata/authorization-server/z-authorization-server-metadata.ts @@ -44,6 +44,9 @@ export const zAuthorizationServerMetadata = z // Attestation Based Client Auth (draft 5) client_attestation_pop_nonce_required: z.boolean().optional(), + + // RFC9396 - Rich Autorization Requests + authorization_details_types_supported: z.array(z.string()).optional(), }) .loose() .refine( diff --git a/packages/openid4vci/src/Openid4vciClient.ts b/packages/openid4vci/src/Openid4vciClient.ts index 6d49a88a..8122cf6b 100644 --- a/packages/openid4vci/src/Openid4vciClient.ts +++ b/packages/openid4vci/src/Openid4vciClient.ts @@ -475,7 +475,7 @@ export class Openid4vciClient { if ( issuerMetadata.originalDraftVersion === Openid4vciDraftVersion.Draft15 || - issuerMetadata.originalDraftVersion === Openid4vciDraftVersion.Draft16 + issuerMetadata.originalDraftVersion === Openid4vciDraftVersion.V1 ) { credentialResponse = await retrieveCredentialsWithCredentialConfigurationId({ accessToken, diff --git a/packages/openid4vci/src/credential-request/credential-response.ts b/packages/openid4vci/src/credential-request/credential-response.ts index 89895922..cbe473fa 100644 --- a/packages/openid4vci/src/credential-request/credential-response.ts +++ b/packages/openid4vci/src/credential-request/credential-response.ts @@ -45,13 +45,25 @@ export function createCredentialResponse(options: CreateCredentialResponseOption } satisfies CredentialResponse) } -export type CreateDeferredCredentialResponseOptions = { - credentials?: DeferredCredentialResponse['credentials'] +export type CreateDeferredCredentialResponseOptions = ( + | { + credentials: DeferredCredentialResponse['credentials'] + notificationId?: string - interval?: number - - notificationId?: string + transactionId?: never + interval?: never + } + | { + /** + * The `transaction_id` that was included in the + */ + transactionId: string + interval: number + credentials?: never + notificationId?: never + } +) & { /** * Additional payload to include in the deferred credential response */ @@ -62,7 +74,10 @@ export function createDeferredCredentialResponse(options: CreateDeferredCredenti return parseWithErrorHandling(zDeferredCredentialResponse, { credentials: options.credentials, notification_id: options.notificationId, + + transaction_id: options.transactionId, interval: options.interval, + ...options.additionalPayload, } satisfies DeferredCredentialResponse) } diff --git a/packages/openid4vci/src/credential-request/format-payload.ts b/packages/openid4vci/src/credential-request/format-payload.ts index 1720ce78..62f08434 100644 --- a/packages/openid4vci/src/credential-request/format-payload.ts +++ b/packages/openid4vci/src/credential-request/format-payload.ts @@ -13,7 +13,7 @@ import { zMsoMdocCredentialIssuerMetadataDraft14, zSdJwtDcCredentialIssuerMetadata, } from '../formats/credential' -import { zLegacySdJwtVcCredentialIssuerMetadataDraft16 } from '../formats/credential/sd-jwt-vc/z-sd-jwt-vc' +import { zLegacySdJwtVcCredentialIssuerMetadataV1 } from '../formats/credential/sd-jwt-vc/z-sd-jwt-vc' import { zSdJwtW3VcCredentialIssuerMetadata } from '../formats/credential/w3c-vc/z-w3c-sd-jwt-vc' import { getCredentialConfigurationSupportedById } from '../metadata/credential-issuer/credential-issuer-metadata' import type { IssuerMetadataResult } from '../metadata/fetch-issuer-metadata' @@ -40,7 +40,7 @@ export function getCredentialRequestFormatPayloadForCredentialConfigurationId( ) if ( - zIs(zLegacySdJwtVcCredentialIssuerMetadataDraft16, credentialConfiguration) || + zIs(zLegacySdJwtVcCredentialIssuerMetadataV1, credentialConfiguration) || zIs(zLegacySdJwtVcCredentialIssuerMetadataDraft14, credentialConfiguration) ) { return { diff --git a/packages/openid4vci/src/credential-request/retrieve-credentials.ts b/packages/openid4vci/src/credential-request/retrieve-credentials.ts index 3cb664b4..d996c9e1 100644 --- a/packages/openid4vci/src/credential-request/retrieve-credentials.ts +++ b/packages/openid4vci/src/credential-request/retrieve-credentials.ts @@ -70,7 +70,7 @@ export async function retrieveCredentialsWithCredentialConfigurationId( ) { if ( options.issuerMetadata.originalDraftVersion !== Openid4vciDraftVersion.Draft15 && - options.issuerMetadata.originalDraftVersion !== Openid4vciDraftVersion.Draft16 + options.issuerMetadata.originalDraftVersion !== Openid4vciDraftVersion.V1 ) { throw new Openid4vciError( 'Requesting credentials based on credential configuration ID is not supported in OpenID4VCI below draft 15. Make sure to provide the format and format specific claims in the request.' @@ -119,10 +119,10 @@ export interface RetrieveCredentialsWithFormatOptions extends RetrieveCredential export async function retrieveCredentialsWithFormat(options: RetrieveCredentialsWithFormatOptions) { if ( options.issuerMetadata.originalDraftVersion === Openid4vciDraftVersion.Draft15 || - options.issuerMetadata.originalDraftVersion === Openid4vciDraftVersion.Draft16 + options.issuerMetadata.originalDraftVersion === Openid4vciDraftVersion.V1 ) { throw new Openid4vciError( - 'Requesting credentials based on format is not supported in OpenID4VCI draft 15. Provide the credential configuration id directly in the request.' + 'Requesting credentials based on format is not supported on OpenID4VCI above draft 15. Provide the credential configuration id directly in the request.' ) } @@ -331,7 +331,11 @@ export async function retrieveDeferredCredentials( // Try to parse the credential response const deferredCredentialResponseResult = isResponseContentType(ContentType.Json, resourceResponse.response) - ? zDeferredCredentialResponse.safeParse(await resourceResponse.response.clone().json()) + ? zDeferredCredentialResponse + .refine((response) => response.credentials || response.transaction_id === options.transactionId, { + error: `Transaction id in deferred credential response does not match transaction id in deferred credential request '${options.transactionId}'`, + }) + .safeParse(await resourceResponse.response.clone().json()) : undefined if (!deferredCredentialResponseResult?.success) { return { diff --git a/packages/openid4vci/src/credential-request/z-credential-response.ts b/packages/openid4vci/src/credential-request/z-credential-response.ts index ae255c8b..defc5d42 100644 --- a/packages/openid4vci/src/credential-request/z-credential-response.ts +++ b/packages/openid4vci/src/credential-request/z-credential-response.ts @@ -13,15 +13,16 @@ const zBaseCredentialResponse = z z.array(zCredentialEncoding), ]) .optional(), - interval: z.number().int().positive().optional(), notification_id: z.string().optional(), + + transaction_id: z.string().optional(), + interval: z.number().int().positive().optional(), }) .loose() export const zCredentialResponse = zBaseCredentialResponse .extend({ credential: z.optional(zCredentialEncoding), - transaction_id: z.string().optional(), c_nonce: z.string().optional(), c_nonce_expires_in: z.number().int().optional(), @@ -65,14 +66,29 @@ export const zCredentialErrorResponse = z export type CredentialErrorResponse = z.infer -export const zDeferredCredentialResponse = zBaseCredentialResponse.refine( - (value) => { - const { credentials, interval } = value - return [credentials, interval].filter((i) => i !== undefined).length === 1 - }, - { - message: `Exactly one of 'credentials' or 'interval' MUST be defined.`, +export const zDeferredCredentialResponse = zBaseCredentialResponse.superRefine((value, ctx) => { + const { credentials, transaction_id, interval, notification_id } = value + + if ([credentials, transaction_id].filter((i) => i !== undefined).length !== 1) { + ctx.addIssue({ + code: 'custom', + message: `Exactly one of 'credentials', or 'transaction_id' MUST be defined.`, + }) + } + + if (transaction_id && !interval) { + ctx.addIssue({ + code: 'custom', + message: `'interval' MUST be defined when 'transaction_id' is defined.`, + }) + } + + if (notification_id && credentials) { + ctx.addIssue({ + code: 'custom', + message: `'notification_id' MUST NOT be defined when 'credentials' is not defined.`, + }) } -) +}) export type DeferredCredentialResponse = z.infer diff --git a/packages/openid4vci/src/formats/credential/sd-jwt-vc/z-sd-jwt-vc.ts b/packages/openid4vci/src/formats/credential/sd-jwt-vc/z-sd-jwt-vc.ts index a98d638d..e3bb170f 100644 --- a/packages/openid4vci/src/formats/credential/sd-jwt-vc/z-sd-jwt-vc.ts +++ b/packages/openid4vci/src/formats/credential/sd-jwt-vc/z-sd-jwt-vc.ts @@ -28,7 +28,7 @@ export type LegacySdJwtVcFormatIdentifier = z.infer -export type CredentialIssuerMetadata = z.infer -const zCredentialIssuerMetadataDraft14Draft15Draft16 = z +export type CredentialIssuerMetadata = z.infer +const zCredentialIssuerMetadataDraft14Draft15V1 = z .object({ credential_issuer: zHttpsUrl, authorization_servers: z.array(zHttpsUrl).optional(), @@ -157,7 +156,6 @@ const zCredentialIssuerMetadataDraft14Draft15Draft16 = z }) .loose() .optional(), - signed_metadata: zCompactJwt.optional(), display: z.array(zCredentialIssuerMetadataDisplayEntry).optional(), credential_configurations_supported: z.record(z.string(), zCredentialConfigurationSupportedWithFormats), }) @@ -258,7 +256,7 @@ export const zCredentialConfigurationSupportedDraft11To16 = z .pipe(zCredentialConfigurationSupportedWithFormats) // Transforms credential configuration supported from draft 16 to draft 15 -const zCredentialConfigurationSupportedDraft16To15 = zCredentialConfigurationSupportedWithFormats.transform( +const zCredentialConfigurationSupportedV1ToDraft15 = zCredentialConfigurationSupportedWithFormats.transform( ({ credential_metadata, ...rest }) => ({ ...credential_metadata, ...rest, @@ -267,7 +265,7 @@ const zCredentialConfigurationSupportedDraft16To15 = zCredentialConfigurationSup // Transforms credential configuration supported to credentials_supported format // Ignores unknown formats -const zCredentialConfigurationSupportedDraft16To11 = zCredentialConfigurationSupportedDraft16To15 +const zCredentialConfigurationSupportedV1ToDraft11 = zCredentialConfigurationSupportedV1ToDraft15 .and( z .object({ @@ -358,7 +356,7 @@ export const zCredentialIssuerMetadataDraft11To16 = z }) .loose() ) - .pipe(zCredentialIssuerMetadataDraft14Draft15Draft16) + .pipe(zCredentialIssuerMetadataDraft14Draft15V1) /** * Typing is a bit off on this one @@ -366,11 +364,11 @@ export const zCredentialIssuerMetadataDraft11To16 = z export type CredentialIssuerMetadataDraft11 = Simplify< CredentialIssuerMetadata & { authorization_server?: string - credentials_supported: z.infer[] + credentials_supported: z.infer[] } > -export const zCredentialIssuerMetadataWithDraft11 = zCredentialIssuerMetadataDraft14Draft15Draft16 +export const zCredentialIssuerMetadataWithDraft11 = zCredentialIssuerMetadataDraft14Draft15V1 .transform((issuerMetadata) => ({ ...issuerMetadata, ...(issuerMetadata.authorization_servers ? { authorization_server: issuerMetadata.authorization_servers[0] } : {}), @@ -380,20 +378,20 @@ export const zCredentialIssuerMetadataWithDraft11 = zCredentialIssuerMetadataDra })), })) .pipe( - zCredentialIssuerMetadataDraft14Draft15Draft16.extend({ - credentials_supported: z.array(zCredentialConfigurationSupportedDraft16To11), + zCredentialIssuerMetadataDraft14Draft15V1.extend({ + credentials_supported: z.array(zCredentialConfigurationSupportedV1ToDraft11), }) ) export const zCredentialIssuerMetadata = z.union([ // First prioritize draft 16/15/14 (and 13) - zCredentialIssuerMetadataDraft14Draft15Draft16, + zCredentialIssuerMetadataDraft14Draft15V1, // Then try parsing draft 11 and transform into draft 16 zCredentialIssuerMetadataDraft11To16, ]) export const zCredentialIssuerMetadataWithDraftVersion = z.union([ - zCredentialIssuerMetadataDraft14Draft15Draft16.transform((credentialIssuerMetadata) => { + zCredentialIssuerMetadataDraft14Draft15V1.transform((credentialIssuerMetadata) => { const credentialConfigurations = Object.values(credentialIssuerMetadata.credential_configurations_supported) const isDraft15 = credentialConfigurations.some((configuration) => { @@ -413,21 +411,20 @@ export const zCredentialIssuerMetadataWithDraftVersion = z.union([ return false }) - const isDraft16 = credentialConfigurations.some((configuration) => { - // Added in draft 16 - return configuration.credential_metadata - }) + // Added in draft 16, but since there's no other breaking changes + // we assume V1 is used when we detect V1 + const isV1 = credentialConfigurations.some((configuration) => configuration.credential_metadata) return { credentialIssuerMetadata, - originalDraftVersion: isDraft16 - ? Openid4vciDraftVersion.Draft16 + originalDraftVersion: isV1 + ? Openid4vciDraftVersion.V1 : isDraft15 ? Openid4vciDraftVersion.Draft15 : Openid4vciDraftVersion.Draft14, } }), - // Then try parsing draft 11 and transform into draft 14 + // Then try parsing draft 11 and transform into draft 16 zCredentialIssuerMetadataDraft11To16.transform((credentialIssuerMetadata) => ({ credentialIssuerMetadata, originalDraftVersion: Openid4vciDraftVersion.Draft11, diff --git a/packages/openid4vci/src/version.ts b/packages/openid4vci/src/version.ts index 52493283..2e8a0555 100644 --- a/packages/openid4vci/src/version.ts +++ b/packages/openid4vci/src/version.ts @@ -1,5 +1,5 @@ export enum Openid4vciDraftVersion { - Draft16 = 'Draft16', + V1 = 'V1', Draft15 = 'Draft15', Draft14 = 'Draft14', Draft11 = 'Draft11',