Skip to content

Commit 83971e7

Browse files
committed
chore: update jwt handling and client assertion logic per RFC 7521/7523 adjustments
1 parent dfe2eb8 commit 83971e7

2 files changed

Lines changed: 16 additions & 6 deletions

File tree

packages/client/lib/AccessTokenClient.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,10 @@ export class AccessTokenClient {
131131
}
132132
const credentialIssuer = opts.credentialIssuer ?? credentialOfferRequest?.credential_offer?.credential_issuer ?? opts.metadata?.issuer
133133
await createJwtBearerClientAssertion(request, { ...opts, credentialIssuer })
134+
// Per RFC 7521, client_id is not needed when client_assertion conveys the client identity
135+
if (request.client_assertion) {
136+
delete request.client_id
137+
}
134138

135139
// Prefer AUTHORIZATION_CODE over PRE_AUTHORIZED_CODE_FLOW
136140
if (!credentialOfferRequest || credentialOfferRequest.supportedFlows.includes(AuthzFlowType.AUTHORIZATION_CODE_FLOW)) {
@@ -147,8 +151,12 @@ export class AccessTokenClient {
147151

148152
if (credentialOfferRequest?.supportedFlows.includes(AuthzFlowType.PRE_AUTHORIZED_CODE_FLOW)) {
149153
this.assertAlphanumericPin(opts.pinMetadata, pin)
150-
request.user_pin = pin
151-
request.tx_code = pin
154+
// OID4VCI 1.0 uses tx_code, older drafts used user_pin
155+
if (opts.pinMetadata?.txCode) {
156+
request.tx_code = pin
157+
} else {
158+
request.user_pin = pin
159+
}
152160

153161
request.grant_type = GrantTypes.PRE_AUTHORIZED_CODE
154162
// we actually know it is there because of the isPreAuthCode call

packages/client/lib/functions/AccessTokenUtil.ts

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ export const createJwtBearerClientAssertion = async (
99
version?: OpenId4VCIVersion
1010
},
1111
): Promise<void> => {
12-
const { asOpts, credentialIssuer } = opts
12+
const { asOpts, credentialIssuer, metadata } = opts
1313
if (asOpts?.clientOpts?.clientAssertionType === 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer') {
1414
const { clientId = request.client_id, signCallbacks, alg } = asOpts.clientOpts
1515
let { kid } = asOpts.clientOpts
@@ -25,6 +25,8 @@ export const createJwtBearerClientAssertion = async (
2525
if (clientId.startsWith('http') && kid.includes('#')) {
2626
kid = kid.split('#')[1]
2727
}
28+
// Per RFC 7523, aud should identify the authorization server (token endpoint or issuer)
29+
const aud = metadata?.token_endpoint ?? asOpts?.tokenEndpoint ?? credentialIssuer
2830
const jwt: Jwt = {
2931
header: {
3032
typ: 'JWT',
@@ -34,10 +36,10 @@ export const createJwtBearerClientAssertion = async (
3436
payload: {
3537
iss: clientId,
3638
sub: clientId,
37-
aud: credentialIssuer,
39+
aud,
3840
jti: uuidv4(),
39-
exp: Math.floor(Date.now()) / 1000 + 60,
40-
iat: Math.floor(Date.now()) / 1000 - 60,
41+
exp: Math.floor(Date.now() / 1000) + 60,
42+
iat: Math.floor(Date.now() / 1000) - 60,
4143
},
4244
}
4345
const pop = await ProofOfPossessionBuilder.fromJwt({

0 commit comments

Comments
 (0)