Skip to content

Commit 919ef7c

Browse files
authored
fix: add missing redirect_uri check (#85)
Signed-off-by: Timo Glastra <timo@animo.id>
1 parent b50af9c commit 919ef7c

5 files changed

Lines changed: 40 additions & 24 deletions

File tree

.changeset/major-lions-roll.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@openid4vc/openid4vp": patch
3+
---
4+
5+
fix: check whether client id identifier matches redirect_uri/resposne_uri when client id prefix is redirect_uri

packages/openid4vp/src/authorization-response/create-authorization-response.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ export async function createOpenid4vpAuthorizationResponse(
103103
// When using OpenID Federation, we must not rely on the client metadata from the request
104104
if (clientIdPrefix === 'openid_federation' && !options.clientMetadata) {
105105
throw new Oauth2Error(
106-
"When OpenID Federation is used as the client id scheme (https/openid_federation), passing externally fetched and verified 'clientMetadata' to the 'createOpenid4vpAuthorizationResponse' is required."
106+
"When OpenID Federation is used as the client id prefix (https/openid_federation), passing externally fetched and verified 'clientMetadata' to the 'createOpenid4vpAuthorizationResponse' is required."
107107
)
108108
}
109109

packages/openid4vp/src/client-identifier-prefix/parse-client-identifier-prefix.ts

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,7 @@ export interface GetOpenid4vpClientIdOptions {
108108
/**
109109
* Get the client id for an authorization request based on the response_mode, client_id, client_id_scheme and origin values.
110110
*
111-
* It will return the client id scheme as used in OpenID4VP draft 29, and optionally provide the legacyClientId if the
111+
* It will return the client id prefix as used in OpenID4VP v1, and optionally provide the legacyClientId if the
112112
* client id was provided with a client_id_scheme
113113
*/
114114
export function getOpenid4vpClientId(options: GetOpenid4vpClientIdOptions): {
@@ -127,7 +127,7 @@ export function getOpenid4vpClientId(options: GetOpenid4vpClientIdOptions): {
127127
clientIdPrefix: UniformClientIdPrefix
128128

129129
/**
130-
* The effective client id scheme, is the client id scheme that was used in the actual request.
130+
* The effective client id prefix, is the client id prefix that was used in the actual request.
131131
*
132132
* E.g. `did` will remain as `did`
133133
*/
@@ -317,7 +317,7 @@ export async function validateOpenid4vpClientId(
317317
if (!parserConfigWithDefaults.supportedSchemes.includes(clientIdPrefix)) {
318318
throw new Oauth2ServerErrorResponseError({
319319
error: Oauth2ErrorCodes.InvalidRequest,
320-
error_description: `Unsupported client identifier scheme. ${clientIdPrefix} is not supported.`,
320+
error_description: `Unsupported client identifier prefix. ${clientIdPrefix} is not supported.`,
321321
})
322322
}
323323

@@ -337,15 +337,15 @@ export async function validateOpenid4vpClientId(
337337
if (!jar) {
338338
throw new Oauth2ServerErrorResponseError({
339339
error: Oauth2ErrorCodes.InvalidRequest,
340-
error_description: 'Using client identifier scheme "https" requires a signed JAR request.',
340+
error_description: 'Using client identifier prefix "https" requires a signed JAR request.',
341341
})
342342
}
343343

344344
if (jar.signer.method !== 'federation') {
345345
throw new Oauth2ServerErrorResponseError({
346346
error: Oauth2ErrorCodes.InvalidRequest,
347347
error_description:
348-
'Something went wrong. The JWT signer method is not federation but the client identifier scheme is https.',
348+
'Something went wrong. The JWT signer method is not federation but the client identifier prefix is https.',
349349
})
350350
}
351351

@@ -362,14 +362,28 @@ export async function validateOpenid4vpClientId(
362362
if (jar) {
363363
throw new Oauth2ServerErrorResponseError({
364364
error: Oauth2ErrorCodes.InvalidRequest,
365-
error_description: 'Using client identifier scheme "redirect_uri" the request MUST NOT be signed.',
365+
error_description: 'Using client identifier prefix "redirect_uri" the request MUST NOT be signed.',
366366
})
367367
}
368368

369369
if (isOpenid4vpAuthorizationRequestDcApi(authorizationRequestPayload)) {
370370
throw new Oauth2ServerErrorResponseError({
371371
error: Oauth2ErrorCodes.InvalidRequest,
372-
error_description: `The client identifier scheme 'redirect_uri' is not supported when using the dc_api response mode.`,
372+
error_description: `The client identifier prefix 'redirect_uri' is not supported when using the dc_api response mode.`,
373+
})
374+
}
375+
376+
if (authorizationRequestPayload.redirect_uri && authorizationRequestPayload.redirect_uri !== clientIdIdentifier) {
377+
throw new Oauth2ServerErrorResponseError({
378+
error: Oauth2ErrorCodes.InvalidClient,
379+
error_description: `When the client identifier prefix is 'redirect_uri', the client id identifier MUST match the redirect_uri.`,
380+
})
381+
}
382+
383+
if (authorizationRequestPayload.response_uri && authorizationRequestPayload.redirect_uri !== clientIdIdentifier) {
384+
throw new Oauth2ServerErrorResponseError({
385+
error: Oauth2ErrorCodes.InvalidClient,
386+
error_description: `When the client identifier prefix is 'redirect_uri', the client id identifier MUST match the response_uri.`,
373387
})
374388
}
375389

@@ -387,15 +401,15 @@ export async function validateOpenid4vpClientId(
387401
if (!jar) {
388402
throw new Oauth2ServerErrorResponseError({
389403
error: Oauth2ErrorCodes.InvalidRequest,
390-
error_description: 'Using client identifier scheme "did" requires a signed JAR request.',
404+
error_description: 'Using client identifier prefix "did" requires a signed JAR request.',
391405
})
392406
}
393407

394408
if (jar.signer.method !== 'did') {
395409
throw new Oauth2ServerErrorResponseError({
396410
error: Oauth2ErrorCodes.InvalidRequest,
397411
error_description:
398-
'Something went wrong. The JWT signer method is not did but the client identifier scheme is did.',
412+
'Something went wrong. The JWT signer method is not did but the client identifier prefix is did.',
399413
})
400414
}
401415

@@ -410,7 +424,7 @@ export async function validateOpenid4vpClientId(
410424
if (clientIdIdentifier !== did) {
411425
throw new Oauth2ServerErrorResponseError({
412426
error: Oauth2ErrorCodes.InvalidRequest,
413-
error_description: `With client identifier scheme '${clientIdPrefix}' the JAR request must be signed by the same DID as the client identifier.`,
427+
error_description: `With client identifier prefix '${clientIdPrefix}' the JAR request must be signed by the same DID as the client identifier.`,
414428
})
415429
}
416430

@@ -428,14 +442,14 @@ export async function validateOpenid4vpClientId(
428442
if (!jar) {
429443
throw new Oauth2ServerErrorResponseError({
430444
error: Oauth2ErrorCodes.InvalidRequest,
431-
error_description: `Using client identifier scheme '${clientIdPrefix}' requires a signed JAR request.`,
445+
error_description: `Using client identifier prefix '${clientIdPrefix}' requires a signed JAR request.`,
432446
})
433447
}
434448

435449
if (jar.signer.method !== 'x5c') {
436450
throw new Oauth2ServerErrorResponseError({
437451
error: Oauth2ErrorCodes.InvalidRequest,
438-
error_description: `Something went wrong. The JWT signer method is not x5c but the client identifier scheme is '${clientIdPrefix}'`,
452+
error_description: `Something went wrong. The JWT signer method is not x5c but the client identifier prefix is '${clientIdPrefix}'`,
439453
})
440454
}
441455

@@ -445,7 +459,7 @@ export async function validateOpenid4vpClientId(
445459
error: Oauth2ErrorCodes.ServerError,
446460
},
447461
{
448-
internalMessage: `Missing required 'getX509CertificateMetadata' callback for verification of '${clientIdPrefix}' client id scheme`,
462+
internalMessage: `Missing required 'getX509CertificateMetadata' callback for verification of '${clientIdPrefix}' client id prefix`,
449463
}
450464
)
451465
}
@@ -525,7 +539,7 @@ export async function validateOpenid4vpClientId(
525539
if (!jar) {
526540
throw new Oauth2ServerErrorResponseError({
527541
error: Oauth2ErrorCodes.InvalidRequest,
528-
error_description: 'Using client identifier scheme "verifier_attestation" requires a signed JAR request.',
542+
error_description: 'Using client identifier prefix "verifier_attestation" requires a signed JAR request.',
529543
})
530544
}
531545
}

packages/openid4vp/src/jar/handle-jar-request/verify-jar-request.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,8 @@ export async function verifyJarRequest(options: VerifyJarRequestOptions): Promis
6161

6262
const sendBy = jarRequestParams.request ? 'value' : 'reference'
6363

64-
// We can't know the client id scheme here if draft was before client_id_scheme became prefix
65-
const clientIdentifierScheme: ClientIdPrefix | undefined = jarRequestParams.client_id
64+
// We can't know the client id prefix here if draft was before client_id_scheme became prefix
65+
const clientIdPrefix: ClientIdPrefix | undefined = jarRequestParams.client_id
6666
? zClientIdPrefix.safeParse(jarRequestParams.client_id.split(':')[0]).data
6767
: 'origin'
6868

@@ -78,7 +78,7 @@ export async function verifyJarRequest(options: VerifyJarRequestOptions): Promis
7878
jarRequestParams.request ??
7979
(await fetchJarRequestObject({
8080
requestUri: jarRequestParams.request_uri,
81-
clientIdentifierScheme,
81+
clientIdPrefix,
8282
method,
8383
wallet,
8484
fetch: callbacks.fetch,

packages/openid4vp/src/jar/jar-request-object/fetch-jar-request-object.ts

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,21 +16,18 @@ import type { WalletMetadata } from '../../models/z-wallet-metadata'
1616
*/
1717
export async function fetchJarRequestObject(options: {
1818
requestUri: string
19-
clientIdentifierScheme?: ClientIdPrefix
19+
clientIdPrefix?: ClientIdPrefix
2020
method: 'get' | 'post'
2121
wallet: {
2222
metadata?: WalletMetadata
2323
nonce?: string
2424
}
2525
fetch?: Fetch
2626
}): Promise<string> {
27-
const { requestUri, clientIdentifierScheme, method, wallet, fetch } = options
27+
const { requestUri, clientIdPrefix, method, wallet, fetch } = options
2828

2929
let requestBody = wallet.metadata ? { wallet_metadata: wallet.metadata, wallet_nonce: wallet.nonce } : undefined
30-
if (
31-
requestBody?.wallet_metadata?.request_object_signing_alg_values_supported &&
32-
clientIdentifierScheme === 'redirect_uri'
33-
) {
30+
if (requestBody?.wallet_metadata?.request_object_signing_alg_values_supported && clientIdPrefix === 'redirect_uri') {
3431
// This value indicates that the Client Identifier (without the prefix redirect_uri:) is the Verifier's Redirect URI (or Response URI when Response Mode direct_post is used). The Authorization Request MUST NOT be signed.
3532
const { request_object_signing_alg_values_supported, ...rest } = requestBody.wallet_metadata
3633
requestBody = { ...requestBody, wallet_metadata: { ...rest } }

0 commit comments

Comments
 (0)