Skip to content

Commit 718aa0d

Browse files
committed
feat: add Client-Initiated Backchannel Authentication
1 parent b93b7fd commit 718aa0d

17 files changed

+1427
-3
lines changed

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ The following features are currently in scope and implemented in this software:
1010

1111
- Authorization Server Metadata discovery
1212
- Authorization Code Flow (profiled under OpenID Connect 1.0, OAuth 2.0, OAuth 2.1, and FAPI 2.0), with PKCE
13-
- Refresh Token, Device Authorization, and Client Credentials Grants
13+
- Refresh Token, Device Authorization, Client-Initiated Backchannel Authentication, and Client Credentials Grants
1414
- Demonstrating Proof-of-Possession at the Application Layer (DPoP)
1515
- Token Introspection and Revocation
1616
- Pushed Authorization Requests (PAR)

docs/README.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,13 @@ Support from the community to continue maintaining and improving this module is
5757
- [clientCredentialsGrantRequest](functions/clientCredentialsGrantRequest.md)
5858
- [processClientCredentialsResponse](functions/processClientCredentialsResponse.md)
5959

60+
## Client-Initiated Backchannel Authentication
61+
62+
- [backchannelAuthenticationGrantRequest](functions/backchannelAuthenticationGrantRequest.md)
63+
- [backchannelAuthenticationRequest](functions/backchannelAuthenticationRequest.md)
64+
- [processBackchannelAuthenticationGrantResponse](functions/processBackchannelAuthenticationGrantResponse.md)
65+
- [processBackchannelAuthenticationResponse](functions/processBackchannelAuthenticationResponse.md)
66+
6067
## DPoP
6168

6269
- [DPoP](functions/DPoP.md)
@@ -184,6 +191,8 @@ Support from the community to continue maintaining and improving this module is
184191

185192
- [AuthorizationDetails](interfaces/AuthorizationDetails.md)
186193
- [AuthorizationServer](interfaces/AuthorizationServer.md)
194+
- [BackchannelAuthenticationRequestOptions](interfaces/BackchannelAuthenticationRequestOptions.md)
195+
- [BackchannelAuthenticationResponse](interfaces/BackchannelAuthenticationResponse.md)
187196
- [Client](interfaces/Client.md)
188197
- [ClientCredentialsGrantRequestOptions](interfaces/ClientCredentialsGrantRequestOptions.md)
189198
- [ConfirmationClaims](interfaces/ConfirmationClaims.md)
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Function: backchannelAuthenticationGrantRequest()
2+
3+
[💗 Help the project](https://github.com/sponsors/panva)
4+
5+
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).
6+
7+
***
8+
9+
**backchannelAuthenticationGrantRequest**(`as`, `client`, `clientAuthentication`, `authReqId`, `options`?): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Response`](https://developer.mozilla.org/docs/Web/API/Response)\>
10+
11+
Performs a Backchannel Authentication Grant request at the
12+
[`as.token_endpoint`](../interfaces/AuthorizationServer.md#token_endpoint).
13+
14+
## Parameters
15+
16+
| Parameter | Type | Description |
17+
| ------ | ------ | ------ |
18+
| `as` | [`AuthorizationServer`](../interfaces/AuthorizationServer.md) | Authorization Server Metadata. |
19+
| `client` | [`Client`](../interfaces/Client.md) | Client Metadata. |
20+
| `clientAuthentication` | [`ClientAuth`](../type-aliases/ClientAuth.md) | Client Authentication Method. |
21+
| `authReqId` | `string` | Unique identifier to identify the authentication request. This is the [`auth_req_id`](../interfaces/BackchannelAuthenticationResponse.md#auth_req_id) retrieved from [processBackchannelAuthenticationResponse](processBackchannelAuthenticationResponse.md). |
22+
| `options`? | [`TokenEndpointRequestOptions`](../interfaces/TokenEndpointRequestOptions.md) | - |
23+
24+
## Returns
25+
26+
[`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Response`](https://developer.mozilla.org/docs/Web/API/Response)\>
27+
28+
## See
29+
30+
- [OpenID Connect Client-Initiated Backchannel Authentication](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#token_request)
31+
- [RFC 9449 - OAuth 2.0 Demonstrating Proof-of-Possession at the Application Layer (DPoP)](https://www.rfc-editor.org/rfc/rfc9449.html#name-dpop-access-token-request)
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Function: backchannelAuthenticationRequest()
2+
3+
[💗 Help the project](https://github.com/sponsors/panva)
4+
5+
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).
6+
7+
***
8+
9+
**backchannelAuthenticationRequest**(`as`, `client`, `clientAuthentication`, `parameters`, `options`?): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Response`](https://developer.mozilla.org/docs/Web/API/Response)\>
10+
11+
Performs a Backchannel Authentication Request at the
12+
[`as.backchannel_authentication_endpoint`](../interfaces/AuthorizationServer.md#backchannel_authentication_endpoint).
13+
14+
## Parameters
15+
16+
| Parameter | Type | Description |
17+
| ------ | ------ | ------ |
18+
| `as` | [`AuthorizationServer`](../interfaces/AuthorizationServer.md) | Authorization Server Metadata. |
19+
| `client` | [`Client`](../interfaces/Client.md) | Client Metadata. |
20+
| `clientAuthentication` | [`ClientAuth`](../type-aliases/ClientAuth.md) | Client Authentication Method. |
21+
| `parameters` | [`Record`](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type)\<`string`, `string`\> \| [`URLSearchParams`](https://developer.mozilla.org/docs/Web/API/URLSearchParams) \| `string`[][] | Backchannel Authentication Request parameters. |
22+
| `options`? | [`BackchannelAuthenticationRequestOptions`](../interfaces/BackchannelAuthenticationRequestOptions.md) | - |
23+
24+
## Returns
25+
26+
[`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Response`](https://developer.mozilla.org/docs/Web/API/Response)\>
27+
28+
## See
29+
30+
[OpenID Connect Client-Initiated Backchannel Authentication](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#auth_request)

docs/functions/deviceCodeGrantRequest.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Performs a Device Authorization Grant request at the
1818
| `as` | [`AuthorizationServer`](../interfaces/AuthorizationServer.md) | Authorization Server Metadata. |
1919
| `client` | [`Client`](../interfaces/Client.md) | Client Metadata. |
2020
| `clientAuthentication` | [`ClientAuth`](../type-aliases/ClientAuth.md) | Client Authentication Method. |
21-
| `deviceCode` | `string` | Device Code. |
21+
| `deviceCode` | `string` | Device Code. This is the [`device_code`](../interfaces/DeviceAuthorizationResponse.md#device_code) retrieved from [processDeviceAuthorizationResponse](processDeviceAuthorizationResponse.md). |
2222
| `options`? | [`TokenEndpointRequestOptions`](../interfaces/TokenEndpointRequestOptions.md) | - |
2323

2424
## Returns
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
# Function: processBackchannelAuthenticationGrantResponse()
2+
3+
[💗 Help the project](https://github.com/sponsors/panva)
4+
5+
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).
6+
7+
***
8+
9+
**processBackchannelAuthenticationGrantResponse**(`as`, `client`, `response`, `options`?): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`TokenEndpointResponse`](../interfaces/TokenEndpointResponse.md)\>
10+
11+
Validates Backchannel Authentication Grant [Response](https://developer.mozilla.org/docs/Web/API/Response) instance to be one coming from the
12+
[`as.token_endpoint`](../interfaces/AuthorizationServer.md#token_endpoint).
13+
14+
## Parameters
15+
16+
| Parameter | Type | Description |
17+
| ------ | ------ | ------ |
18+
| `as` | [`AuthorizationServer`](../interfaces/AuthorizationServer.md) | Authorization Server Metadata. |
19+
| `client` | [`Client`](../interfaces/Client.md) | Client Metadata. |
20+
| `response` | [`Response`](https://developer.mozilla.org/docs/Web/API/Response) | Resolved value from [backchannelAuthenticationGrantRequest](backchannelAuthenticationGrantRequest.md). |
21+
| `options`? | [`JWEDecryptOptions`](../interfaces/JWEDecryptOptions.md) | - |
22+
23+
## Returns
24+
25+
[`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`TokenEndpointResponse`](../interfaces/TokenEndpointResponse.md)\>
26+
27+
Resolves with an object representing the parsed successful response. OAuth 2.0 protocol
28+
style errors are rejected using [ResponseBodyError](../classes/ResponseBodyError.md). WWW-Authenticate HTTP Header
29+
challenges are rejected with [WWWAuthenticateChallengeError](../classes/WWWAuthenticateChallengeError.md).
30+
31+
## See
32+
33+
[OpenID Connect Client-Initiated Backchannel Authentication](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#token_request)
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Function: processBackchannelAuthenticationResponse()
2+
3+
[💗 Help the project](https://github.com/sponsors/panva)
4+
5+
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).
6+
7+
***
8+
9+
**processBackchannelAuthenticationResponse**(`as`, `client`, `response`): [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`BackchannelAuthenticationResponse`](../interfaces/BackchannelAuthenticationResponse.md)\>
10+
11+
Validates [Response](https://developer.mozilla.org/docs/Web/API/Response) instance to be one coming from the
12+
[`as.backchannel_authentication_endpoint`](../interfaces/AuthorizationServer.md#backchannel_authentication_endpoint).
13+
14+
## Parameters
15+
16+
| Parameter | Type | Description |
17+
| ------ | ------ | ------ |
18+
| `as` | [`AuthorizationServer`](../interfaces/AuthorizationServer.md) | Authorization Server Metadata. |
19+
| `client` | [`Client`](../interfaces/Client.md) | Client Metadata. |
20+
| `response` | [`Response`](https://developer.mozilla.org/docs/Web/API/Response) | Resolved value from [backchannelAuthenticationRequest](backchannelAuthenticationRequest.md). |
21+
22+
## Returns
23+
24+
[`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`BackchannelAuthenticationResponse`](../interfaces/BackchannelAuthenticationResponse.md)\>
25+
26+
Resolves with an object representing the parsed successful response. OAuth 2.0 protocol
27+
style errors are rejected using [ResponseBodyError](../classes/ResponseBodyError.md). WWW-Authenticate HTTP Header
28+
challenges are rejected with [WWWAuthenticateChallengeError](../classes/WWWAuthenticateChallengeError.md).
29+
30+
## See
31+
32+
[OpenID Connect Client-Initiated Backchannel Authentication](https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0.html#auth_request)
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Interface: BackchannelAuthenticationRequestOptions
2+
3+
[💗 Help the project](https://github.com/sponsors/panva)
4+
5+
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).
6+
7+
***
8+
9+
## Properties
10+
11+
### ~~\[allowInsecureRequests\]?~~
12+
13+
`optional` **\[allowInsecureRequests\]**: `boolean`
14+
15+
See [allowInsecureRequests](../variables/allowInsecureRequests.md).
16+
17+
#### Deprecated
18+
19+
***
20+
21+
### \[customFetch\]()?
22+
23+
`optional` **\[customFetch\]**: (`url`, `options`) => [`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Response`](https://developer.mozilla.org/docs/Web/API/Response)\>
24+
25+
See [customFetch](../variables/customFetch.md).
26+
27+
#### Parameters
28+
29+
| Parameter | Type | Description |
30+
| ------ | ------ | ------ |
31+
| `url` | `string` | URL the request is being made sent to [fetch](https://developer.mozilla.org/docs/Web/API/Window/fetch) as the `resource` argument |
32+
| `options` | [`CustomFetchOptions`](CustomFetchOptions.md)\<`"POST"`, [`URLSearchParams`](https://developer.mozilla.org/docs/Web/API/URLSearchParams)\> | Options otherwise sent to [fetch](https://developer.mozilla.org/docs/Web/API/Window/fetch) as the `options` argument |
33+
34+
#### Returns
35+
36+
[`Promise`](https://developer.mozilla.org/docs/Web/JavaScript/Reference/Global_Objects/Promise)\<[`Response`](https://developer.mozilla.org/docs/Web/API/Response)\>
37+
38+
***
39+
40+
### headers?
41+
42+
`optional` **headers**: [`Record`](https://www.typescriptlang.org/docs/handbook/utility-types.html#recordkeys-type)\<`string`, `string`\> \| [`string`, `string`][] \| [`Headers`](https://developer.mozilla.org/docs/Web/API/Headers)
43+
44+
Headers to additionally send with the HTTP request(s) triggered by this function's invocation.
45+
46+
***
47+
48+
### signal?
49+
50+
`optional` **signal**: [`AbortSignal`](https://developer.mozilla.org/docs/Web/API/AbortSignal) \| () => [`AbortSignal`](https://developer.mozilla.org/docs/Web/API/AbortSignal)
51+
52+
An AbortSignal instance, or a factory returning one, to abort the HTTP request(s) triggered by
53+
this function's invocation.
54+
55+
#### Example
56+
57+
A 5000ms timeout AbortSignal for every request
58+
59+
```js
60+
let signal = () => AbortSignal.timeout(5_000) // Note: AbortSignal.timeout may not yet be available in all runtimes.
61+
```
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
# Interface: BackchannelAuthenticationResponse
2+
3+
[💗 Help the project](https://github.com/sponsors/panva)
4+
5+
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).
6+
7+
***
8+
9+
## Indexable
10+
11+
\[`parameter`: `string`\]: `undefined` \| [`JsonValue`](../type-aliases/JsonValue.md)
12+
13+
## Properties
14+
15+
### auth\_req\_id
16+
17+
`readonly` **auth\_req\_id**: `string`
18+
19+
Unique identifier to identify the authentication request.
20+
21+
***
22+
23+
### expires\_in
24+
25+
`readonly` **expires\_in**: `number`
26+
27+
The lifetime in seconds of the "auth_req_id"
28+
29+
***
30+
31+
### interval?
32+
33+
`readonly` `optional` **interval**: `number`
34+
35+
The minimum amount of time in seconds that the client should wait between polling requests to
36+
the token endpoint.
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
import * as oauth from 'oauth4webapi'
2+
3+
// Prerequisites
4+
5+
let issuer!: URL // Authorization server's Issuer Identifier URL
6+
let algorithm!:
7+
| 'oauth2' /* For .well-known/oauth-authorization-server discovery */
8+
| 'oidc' /* For .well-known/openid-configuration discovery */
9+
| undefined /* Defaults to 'oidc' */
10+
let client_id!: string
11+
12+
// End of prerequisites
13+
14+
const as = await oauth
15+
.discoveryRequest(issuer, { algorithm })
16+
.then((response) => oauth.processDiscoveryResponse(issuer, response))
17+
18+
const client: oauth.Client = { client_id }
19+
const clientAuth = oauth.None()
20+
21+
let auth_req_id: string
22+
let interval: number
23+
24+
// Backchannel Authentication Request & Response
25+
{
26+
const parameters = new URLSearchParams()
27+
parameters.set('scope', 'openid')
28+
29+
const response = await oauth.backchannelAuthenticationRequest(as, client, clientAuth, parameters)
30+
31+
const result = await oauth.processBackchannelAuthenticationResponse(as, client, response)
32+
33+
console.log('Backchannel Authentication Response', result)
34+
;({ auth_req_id, interval = 5 } = result)
35+
}
36+
37+
// Backchannel Authentication Grant Request & Response
38+
let access_token: string
39+
let sub: string
40+
{
41+
let success: oauth.TokenEndpointResponse | undefined = undefined
42+
function wait() {
43+
return new Promise((resolve) => {
44+
setTimeout(resolve, interval * 1000)
45+
})
46+
}
47+
48+
while (success === undefined) {
49+
await wait()
50+
const response = await oauth.backchannelAuthenticationGrantRequest(
51+
as,
52+
client,
53+
clientAuth,
54+
auth_req_id,
55+
)
56+
57+
success = await oauth
58+
.processBackchannelAuthenticationGrantResponse(as, client, response)
59+
.catch((err) => {
60+
if (err instanceof oauth.ResponseBodyError) {
61+
switch (err.error) {
62+
case 'slow_down':
63+
interval += 5
64+
case 'authorization_pending':
65+
return undefined
66+
}
67+
}
68+
throw err
69+
})
70+
}
71+
72+
console.log('Access Token Response', success)
73+
;({ access_token } = success)
74+
const claims = oauth.getValidatedIdTokenClaims(success)!
75+
console.log('ID Token Claims', claims)
76+
;({ sub } = claims)
77+
}
78+
79+
// UserInfo Request
80+
{
81+
const response = await oauth.userInfoRequest(as, client, access_token)
82+
83+
const result = await oauth.processUserInfoResponse(as, client, sub, response)
84+
console.log('UserInfo Response', result)
85+
}

0 commit comments

Comments
 (0)