Skip to content

Commit 951647d

Browse files
committed
fix: Do not send client id in pre-auth when no other form of client auth is used
1 parent 8dbfff9 commit 951647d

2 files changed

Lines changed: 80 additions & 0 deletions

File tree

packages/client/lib/AccessTokenClient.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,13 @@ export class AccessTokenClient {
162162
// we actually know it is there because of the isPreAuthCode call
163163
request[PRE_AUTH_CODE_LITERAL] = credentialOfferRequest?.credential_offer.grants?.[PRE_AUTH_GRANT_LITERAL]?.[PRE_AUTH_CODE_LITERAL]
164164

165+
// OID4VCI 1.0 (Token Request): for the pre-authorized code grant client authentication is OPTIONAL and client_id
166+
// "is only needed when a form of Client Authentication that relies on the parameter is used", so omit it unless
167+
// such a method is in play (client_assertion was handled above) or it was explicitly passed as additional param
168+
if (request.client_id && !request.client_secret && opts.additionalParams?.client_id === undefined) {
169+
delete request.client_id
170+
}
171+
165172
return request as AccessTokenRequest
166173
}
167174

packages/client/lib/__tests__/AccessTokenClient.spec.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,4 +235,77 @@ describe('AccessTokenClient should', () => {
235235
}),
236236
).toThrow(Error('Cannot determine token URL if no issuer, metadata and no Authorization Server values are present'))
237237
})
238+
239+
it(
240+
'not include client_id in a pre-authorized code token request when no client authentication is used',
241+
async () => {
242+
const accessTokenClient: AccessTokenClient = new AccessTokenClient()
243+
244+
const accessTokenRequest = await accessTokenClient.createAccessTokenRequest({
245+
credentialOffer: INITIATION_TEST,
246+
pin: '1234',
247+
pinMetadata: { isPinRequired: true, txCode: INITIATION_TEST.txCode },
248+
asOpts: { clientOpts: { clientId: 'https://sphereon.com/ssi-wallet' } },
249+
})
250+
251+
expect(accessTokenRequest.grant_type).toEqual(GrantTypes.PRE_AUTHORIZED_CODE)
252+
expect(accessTokenRequest.client_id).toBeUndefined()
253+
},
254+
UNIT_TEST_TIMEOUT,
255+
)
256+
257+
it(
258+
'include client_id in a pre-authorized code token request when client authentication relying on it is used',
259+
async () => {
260+
const accessTokenClient: AccessTokenClient = new AccessTokenClient()
261+
262+
const accessTokenRequest = await accessTokenClient.createAccessTokenRequest({
263+
credentialOffer: INITIATION_TEST,
264+
pin: '1234',
265+
pinMetadata: { isPinRequired: true, txCode: INITIATION_TEST.txCode },
266+
asOpts: { clientOpts: { clientId: 'my-client' } },
267+
additionalParams: { client_secret: 'my-secret' },
268+
})
269+
270+
expect(accessTokenRequest.grant_type).toEqual(GrantTypes.PRE_AUTHORIZED_CODE)
271+
expect(accessTokenRequest.client_id).toEqual('my-client')
272+
expect(accessTokenRequest.client_secret).toEqual('my-secret')
273+
},
274+
UNIT_TEST_TIMEOUT,
275+
)
276+
277+
it(
278+
'include client_id in a pre-authorized code token request when explicitly provided as additional param',
279+
async () => {
280+
const accessTokenClient: AccessTokenClient = new AccessTokenClient()
281+
282+
const accessTokenRequest = await accessTokenClient.createAccessTokenRequest({
283+
credentialOffer: INITIATION_TEST,
284+
pin: '1234',
285+
pinMetadata: { isPinRequired: true, txCode: INITIATION_TEST.txCode },
286+
additionalParams: { client_id: 'forced-client-id' },
287+
})
288+
289+
expect(accessTokenRequest.grant_type).toEqual(GrantTypes.PRE_AUTHORIZED_CODE)
290+
expect(accessTokenRequest.client_id).toEqual('forced-client-id')
291+
},
292+
UNIT_TEST_TIMEOUT,
293+
)
294+
295+
it(
296+
'include client_id in an authorization code token request for a public client without client authentication',
297+
async () => {
298+
const accessTokenClient: AccessTokenClient = new AccessTokenClient()
299+
300+
const accessTokenRequest = await accessTokenClient.createAccessTokenRequest({
301+
code: '9mq3kwIuNZ88czRjJ2-UDxtaNXulOfxHSXo-kM01MLV',
302+
redirectUri: 'http://test.com/cb',
303+
asOpts: { clientOpts: { clientId: 'test-client' } },
304+
})
305+
306+
expect(accessTokenRequest.grant_type).toEqual(GrantTypes.AUTHORIZATION_CODE)
307+
expect(accessTokenRequest.client_id).toEqual('test-client')
308+
},
309+
UNIT_TEST_TIMEOUT,
310+
)
238311
})

0 commit comments

Comments
 (0)