Skip to content

Commit 2fb51e1

Browse files
committed
docs: add DPoP example
1 parent 2e152d9 commit 2fb51e1

File tree

5 files changed

+156
-0
lines changed

5 files changed

+156
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import * as client from 'openid-client'
5454
- Authorization Code Flow (OAuth 2.0) - [source](examples/oauth.ts)
5555
- Authorization Code Flow (OpenID Connect) - [source](examples/oidc.ts) | [diff](examples/oidc.diff)
5656
- Extensions
57+
- DPoP - [source](examples/dpop.ts) | [diff](examples/dpop.diff)
5758
- JWT Secured Authorization Request (JAR) - [source](examples/jar.ts) | [diff](examples/jar.diff)
5859
- JWT Secured Authorization Response Mode (JARM) - [source](examples/jarm.ts) | [diff](examples/jarm.diff)
5960
- Pushed Authorization Request (PAR) - [source](examples/par.ts) | [diff](examples/par.diff)

examples/.update-diffs.sh

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
git diff HEAD:examples/oauth.ts examples/oidc.ts > examples/oidc.diff
22
git diff HEAD:examples/oauth.ts examples/par.ts > examples/par.diff
3+
git diff HEAD:examples/oauth.ts examples/dpop.ts > examples/dpop.diff
34
git diff HEAD:examples/oauth.ts examples/jar.ts > examples/jar.diff
45
git diff HEAD:examples/oauth.ts examples/jarm.ts > examples/jarm.diff

examples/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ A collection of examples for the most common use cases.
1919
- Authorization Code Flow (OAuth 2.0) - [source](oauth.ts)
2020
- Authorization Code Flow (OpenID Connect) - [source](oidc.ts) | [diff](oidc.diff)
2121
- Extensions
22+
- DPoP - [source](dpop.ts) | [diff](dpop.diff)
2223
- JWT Secured Authorization Request (JAR) - [source](jar.ts) | [diff](jar.diff)
2324
- JWT Secured Authorization Response Mode (JARM) - [source](jarm.ts) | [diff](jarm.diff)
2425
- Pushed Authorization Request (PAR) - [source](par.ts) | [diff](par.diff)

examples/dpop.diff

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
diff --git a/examples/oauth.ts b/examples/dpop.ts
2+
index dde3bbc..dcd9ff2 100644
3+
--- a/examples/oauth.ts
4+
+++ b/examples/dpop.ts
5+
@@ -12,10 +12,19 @@ let clientSecret!: string
6+
*/
7+
let redirect_uri!: string
8+
9+
+/**
10+
+ * In order to take full advantage of DPoP you shall generate a random key pair
11+
+ * for every session. In the browser environment you shall use IndexedDB to
12+
+ * persist the generated CryptoKeyPair.
13+
+ */
14+
+let DPoPKeys!: client.CryptoKeyPair
15+
+
16+
// End of prerequisites
17+
18+
let config = await client.discovery(server, clientId, clientSecret)
19+
20+
+let DPoP = client.getDPoPHandle(config, DPoPKeys)
21+
+
22+
let code_challenge_method = 'S256'
23+
/**
24+
* The following (code_verifier and potentially state) MUST be generated for
25+
@@ -58,10 +67,16 @@ let state!: string
26+
let access_token: string
27+
{
28+
let currentUrl: URL = getCurrentUrl()
29+
- let tokens = await client.authorizationCodeGrant(config, currentUrl, {
30+
- pkceCodeVerifier: code_verifier,
31+
- expectedState: state,
32+
- })
33+
+ let tokens = await client.authorizationCodeGrant(
34+
+ config,
35+
+ currentUrl,
36+
+ {
37+
+ pkceCodeVerifier: code_verifier,
38+
+ expectedState: state,
39+
+ },
40+
+ undefined,
41+
+ { DPoP },
42+
+ )
43+
44+
console.log('Token Endpoint Response', tokens)
45+
;({ access_token } = tokens)
46+
@@ -74,6 +89,9 @@ let access_token: string
47+
access_token,
48+
new URL('https://rs.example.com/api'),
49+
'GET',
50+
+ undefined,
51+
+ undefined,
52+
+ { DPoP },
53+
)
54+
55+
console.log('Protected Resource Response', await protectedResource.json())

examples/dpop.ts

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import * as client from 'openid-client'
2+
3+
// Prerequisites
4+
5+
let getCurrentUrl!: (...args: any) => URL
6+
let server!: URL // Authorization server's Issuer Identifier URL
7+
let clientId!: string
8+
let clientSecret!: string
9+
/**
10+
* Value used in the authorization request as redirect_uri pre-registered at the
11+
* Authorization Server.
12+
*/
13+
let redirect_uri!: string
14+
15+
/**
16+
* In order to take full advantage of DPoP you shall generate a random key pair
17+
* for every session. In the browser environment you shall use IndexedDB to
18+
* persist the generated CryptoKeyPair.
19+
*/
20+
let DPoPKeys!: client.CryptoKeyPair
21+
22+
// End of prerequisites
23+
24+
let config = await client.discovery(server, clientId, clientSecret)
25+
26+
let DPoP = client.getDPoPHandle(config, DPoPKeys)
27+
28+
let code_challenge_method = 'S256'
29+
/**
30+
* The following (code_verifier and potentially state) MUST be generated for
31+
* every redirect to the authorization_endpoint. You must store the
32+
* code_verifier and state in the end-user session such that it can be recovered
33+
* as the user gets redirected from the authorization server back to your
34+
* application.
35+
*/
36+
let code_verifier = client.randomPKCECodeVerifier()
37+
let code_challenge = await client.calculatePKCECodeChallenge(code_verifier)
38+
let state!: string
39+
40+
{
41+
// redirect user to as.authorization_endpoint
42+
let parameters: Record<string, string> = {
43+
redirect_uri,
44+
scope: 'api:read',
45+
code_challenge,
46+
code_challenge_method,
47+
}
48+
49+
/**
50+
* We cannot be sure the AS supports PKCE so we're going to use state too. Use
51+
* of PKCE is backwards compatible even if the AS doesn't support it which is
52+
* why we're using it regardless.
53+
*/
54+
if (!config.serverMetadata().supportsPKCE()) {
55+
state = client.randomState()
56+
parameters.state = state
57+
}
58+
59+
let redirectTo = client.buildAuthorizationUrl(config, parameters)
60+
61+
console.log('redirecting to', redirectTo.href)
62+
// now redirect the user to redirectTo.href
63+
}
64+
65+
// one eternity later, the user lands back on the redirect_uri
66+
// Authorization Code Grant
67+
let access_token: string
68+
{
69+
let currentUrl: URL = getCurrentUrl()
70+
let tokens = await client.authorizationCodeGrant(
71+
config,
72+
currentUrl,
73+
{
74+
pkceCodeVerifier: code_verifier,
75+
expectedState: state,
76+
},
77+
undefined,
78+
{ DPoP },
79+
)
80+
81+
console.log('Token Endpoint Response', tokens)
82+
;({ access_token } = tokens)
83+
}
84+
85+
// Protected Resource Request
86+
{
87+
let protectedResource = await client.fetchProtectedResource(
88+
config,
89+
access_token,
90+
new URL('https://rs.example.com/api'),
91+
'GET',
92+
undefined,
93+
undefined,
94+
{ DPoP },
95+
)
96+
97+
console.log('Protected Resource Response', await protectedResource.json())
98+
}

0 commit comments

Comments
 (0)