Skip to content

Commit

Permalink
feat: add a server metadata helper for checking PKCE support
Browse files Browse the repository at this point in the history
  • Loading branch information
panva committed Oct 17, 2024
1 parent a704534 commit ca34a91
Show file tree
Hide file tree
Showing 11 changed files with 79 additions and 42 deletions.
5 changes: 1 addition & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,7 @@ let parameters: Record<string, string> = {
code_challenge_method: 'S256',
}

if (
config.serverMetadata().code_challenge_methods_supported?.includes('S256') !==
true
) {
if (!config.serverMetadata().supportsPKCE()) {
/**
* We cannot be sure the server supports PKCE so we're going to use state too.
* Use of PKCE is backwards compatible even if the AS doesn't support it which
Expand Down
18 changes: 10 additions & 8 deletions conformance/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,14 +295,16 @@ export const flow = (options?: MacroOptions) => {
? lib.getDPoPHandle(client, await lib.randomDPoPKeyPair(ALG))
: undefined

let code_challenge: string | undefined
let code_verifier: string | undefined
let code_challenge_method: string | undefined

if (response_type.includes('code')) {
code_verifier = lib.randomPKCECodeVerifier()
code_challenge = await lib.calculatePKCECodeChallenge(code_verifier)
code_challenge_method = 'S256'
const code_verifier = lib.randomPKCECodeVerifier()
const code_challenge = await lib.calculatePKCECodeChallenge(code_verifier)
const code_challenge_method = 'S256'

if (
!client.serverMetadata().supportsPKCE() &&
!response_type.includes('id_token')
) {
options ||= {}
options.useState = true
}

const scope = getScope(variant)
Expand Down
1 change: 1 addition & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@ Support from the community to continue maintaining and improving this module is
- [ModifyAssertionOptions](interfaces/ModifyAssertionOptions.md)
- [MTLSEndpointAliases](interfaces/MTLSEndpointAliases.md)
- [PrivateKey](interfaces/PrivateKey.md)
- [ServerMetadataHelpers](interfaces/ServerMetadataHelpers.md)
- [TokenEndpointResponse](interfaces/TokenEndpointResponse.md)
- [TokenEndpointResponseHelpers](interfaces/TokenEndpointResponseHelpers.md)
- [UserInfoAddress](interfaces/UserInfoAddress.md)
Expand Down
4 changes: 2 additions & 2 deletions docs/interfaces/ConfigurationMethods.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,10 @@ Public methods available on a [Configuration](../classes/Configuration.md) insta

### serverMetadata()

**serverMetadata**(): [`Readonly`](https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype)\<[`ServerMetadata`](ServerMetadata.md)\>
**serverMetadata**(): [`Readonly`](https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype)\<[`ServerMetadata`](ServerMetadata.md)\> & [`ServerMetadataHelpers`](ServerMetadataHelpers.md)

Used to retrieve the Authorization Server Metadata

#### Returns

[`Readonly`](https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype)\<[`ServerMetadata`](ServerMetadata.md)\>
[`Readonly`](https://www.typescriptlang.org/docs/handbook/utility-types.html#readonlytype)\<[`ServerMetadata`](ServerMetadata.md)\> & [`ServerMetadataHelpers`](ServerMetadataHelpers.md)
26 changes: 26 additions & 0 deletions docs/interfaces/ServerMetadataHelpers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# Interface: ServerMetadataHelpers

[💗 Help the project](https://github.com/sponsors/panva)

Support from the community to continue maintaining and improving this module is welcome. If you find the module useful, please consider supporting the project by [becoming a sponsor](https://github.com/sponsors/panva).

***

## Methods

### supportsPKCE()

**supportsPKCE**(`method`?): `boolean`

Determines whether the Authorization Server supports a given Code Challenge
Method

#### Parameters

| Parameter | Type | Description |
| ------ | ------ | ------ |
| `method`? | `string` | Code Challenge Method. Default is `S256` |

#### Returns

`boolean`
6 changes: 1 addition & 5 deletions examples/jar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,11 +43,7 @@ let state!: string
* of PKCE is backwards compatible even if the AS doesn't support it which is
* why we're using it regardless.
*/
if (
config
.serverMetadata()
.code_challenge_methods_supported?.includes('S256') !== true
) {
if (!config.serverMetadata().supportsPKCE()) {
state = client.randomState()
parameters.state = state
}
Expand Down
6 changes: 1 addition & 5 deletions examples/jarm.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,11 +44,7 @@ let state!: string
* of PKCE is backwards compatible even if the AS doesn't support it which is
* why we're using it regardless.
*/
if (
config
.serverMetadata()
.code_challenge_methods_supported?.includes('S256') !== true
) {
if (!config.serverMetadata().supportsPKCE()) {
state = client.randomState()
parameters.state = state
}
Expand Down
6 changes: 1 addition & 5 deletions examples/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,7 @@ let state!: string
* of PKCE is backwards compatible even if the AS doesn't support it which is
* why we're using it regardless.
*/
if (
config
.serverMetadata()
.code_challenge_methods_supported?.includes('S256') !== true
) {
if (!config.serverMetadata().supportsPKCE()) {
state = client.randomState()
parameters.state = state
}
Expand Down
6 changes: 1 addition & 5 deletions examples/oidc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,7 @@ let nonce!: string
* of PKCE is backwards compatible even if the AS doesn't support it which is
* why we're using it regardless.
*/
if (
config
.serverMetadata()
.code_challenge_methods_supported?.includes('S256') !== true
) {
if (!config.serverMetadata().supportsPKCE()) {
nonce = client.randomNonce()
parameters.nonce = nonce
}
Expand Down
6 changes: 1 addition & 5 deletions examples/par.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,7 @@ let state!: string
* of PKCE is backwards compatible even if the AS doesn't support it which is
* why we're using it regardless.
*/
if (
config
.serverMetadata()
.code_challenge_methods_supported?.includes('S256') !== true
) {
if (!config.serverMetadata().supportsPKCE()) {
state = client.randomState()
parameters.state = state
}
Expand Down
37 changes: 34 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1449,6 +1449,35 @@ async function decrypt(
)
}

export interface ServerMetadataHelpers {
/**
* Determines whether the Authorization Server supports a given Code Challenge
* Method
*
* @param method Code Challenge Method. Default is `S256`
*/
supportsPKCE(method?: string): boolean
}

function getServerHelpers(metadata: Readonly<ServerMetadata>) {
return {
supportsPKCE: {
__proto__: null,
value(method = 'S256') {
return (
metadata.code_challenge_methods_supported?.includes(method) !== true
)
},
},
}
}

function addServerHelpers(
metadata: Readonly<ServerMetadata>,
): asserts metadata is typeof metadata & ServerMetadataHelpers {
Object.defineProperties(metadata, getServerHelpers(metadata))
}

// private
const kEntraId: unique symbol = Symbol()

Expand All @@ -1471,7 +1500,7 @@ export interface ConfigurationMethods {
/**
* Used to retrieve the Authorization Server Metadata
*/
serverMetadata(): Readonly<ServerMetadata>
serverMetadata(): Readonly<ServerMetadata> & ServerMetadataHelpers
}

/**
Expand Down Expand Up @@ -1651,8 +1680,10 @@ export class Configuration
/**
* @ignore
*/
serverMetadata(): Readonly<ServerMetadata> {
return structuredClone(int(this).as)
serverMetadata(): Readonly<ServerMetadata> & ServerMetadataHelpers {
const metadata = structuredClone(int(this).as)
addServerHelpers(metadata)
return metadata
}

/**
Expand Down

0 comments on commit ca34a91

Please sign in to comment.