Skip to content

Commit

Permalink
refactor!: private_key_jwt audience is now only the issuer identifier
Browse files Browse the repository at this point in the history
BREAKING CHANGE: the audience of a Private Key JWT and Client Secret JWT
client assertions is now just the issuer identifier
  • Loading branch information
panva committed Oct 7, 2024
1 parent fe11bdc commit f388ba8
Show file tree
Hide file tree
Showing 3 changed files with 30 additions and 28 deletions.
15 changes: 11 additions & 4 deletions conformance/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,10 +196,17 @@ export const flow = (options?: MacroOptions) => {
break
case 'private_key_jwt':
const [jwk] = configuration.client.jwks.keys
clientAuth = oauth.PrivateKeyJwt({
kid: jwk.kid,
key: await importPrivateKey(ALG, jwk),
})
clientAuth = oauth.PrivateKeyJwt(
{
kid: jwk.kid,
key: await importPrivateKey(ALG, jwk),
},
{
[oauth.modifyAssertion]: (_header, payload) => {
payload.aud = [as.issuer, as.token_endpoint!]
},
},
)
break
case 'client_secret_basic':
clientAuth = oauth.ClientSecretBasic(configuration.client.client_secret!)
Expand Down
39 changes: 17 additions & 22 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -279,7 +279,7 @@ export const customFetch: unique symbol = Symbol()
*
* @example
*
* Changing Private Key JWT client assertion audience issued from an array to a string
* Changing Private Key JWT client assertion audience issued from a string to an array
*
* ```ts
* let as!: oauth.AuthorizationServer
Expand All @@ -289,7 +289,7 @@ export const customFetch: unique symbol = Symbol()
*
* let clientAuth = oauth.PrivateKeyJwt(key, {
* [oauth.modifyAssertion](header, payload) {
* payload.aud = as.issuer
* payload.aud = [as.issuer, as.token_endpoint!]
* },
* })
*
Expand Down Expand Up @@ -1644,6 +1644,19 @@ export interface ModifyAssertionOptions {
[modifyAssertion]?: ModifyAssertionFunction
}

function clientAssertionPayload(as: AuthorizationServer, client: Client) {
const now = epochTime() + getClockSkew(client)
return {
jti: randomBytes(),
aud: as.issuer,
exp: now + 60,
iat: now,
nbf: now,
iss: client.client_id,
sub: client.client_id,
}
}

/**
* **`private_key_jwt`** uses the HTTP request body to send `client_id`, `client_assertion_type`,
* and `client_assertion` as `application/x-www-form-urlencoded` body parameters. Digital signature
Expand Down Expand Up @@ -1672,16 +1685,7 @@ export function PrivateKeyJwt(
assertPrivateKey(key, '"clientPrivateKey.key"')
return async (as, client, body, _headers) => {
const header = { alg: keyToJws(key), kid }
const now = epochTime() + getClockSkew(client)
const payload = {
jti: randomBytes(),
aud: [as.issuer, as.token_endpoint!],
exp: now + 60,
iat: now,
nbf: now,
iss: client.client_id,
sub: client.client_id,
}
const payload = clientAssertionPayload(as, client)

options?.[modifyAssertion]?.(header, payload)

Expand Down Expand Up @@ -1729,16 +1733,7 @@ export function ClientSecretJwt(
)

const header = { alg: 'HS256' }
const now = epochTime() + getClockSkew(client)
const payload = {
jti: randomBytes(),
aud: [as.issuer, as.token_endpoint!],
exp: now + 60,
iat: now,
nbf: now,
iss: client.client_id,
sub: client.client_id,
}
const payload = clientAssertionPayload(as, client)

modify?.(header, payload)

Expand Down
4 changes: 2 additions & 2 deletions test/client_auth.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@ test('private_key_jwt', async (t) => {
)

const assertion = jose.decodeJwt(params.get('client_assertion')!)
t.deepEqual(assertion.aud, [tIssuer.issuer, tIssuer.token_endpoint])
t.is(assertion.aud, tIssuer.issuer)
t.is(assertion.iss, client.client_id)
t.is(assertion.sub, client.client_id)
t.is(typeof assertion.exp, 'number')
Expand Down Expand Up @@ -186,7 +186,7 @@ test('private_key_jwt ({ key: CryptoKey, kid: string })', async (t) => {
)

const assertion = jose.decodeJwt(params.get('client_assertion')!)
t.deepEqual(assertion.aud, [tIssuer.issuer, tIssuer.token_endpoint])
t.is(assertion.aud, tIssuer.issuer)
t.is(assertion.iss, client.client_id)
t.is(assertion.sub, client.client_id)
t.is(typeof assertion.exp, 'number')
Expand Down

0 comments on commit f388ba8

Please sign in to comment.