forked from panva/oauth4webapi
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathpar.ts
142 lines (121 loc) · 4.64 KB
/
par.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
import * as oauth from '../src/index.js' // replace with an import of oauth4webapi
// Prerequisites
let issuer!: URL // Authorization server's Issuer Identifier URL
let algorithm!:
| 'oauth2' /* For .well-known/oauth-authorization-server discovery */
| 'oidc' /* For .well-known/openid-configuration discovery */
| undefined /* Defaults to 'oidc' */
let client_id!: string
let client_secret!: string
/**
* Value used in the authorization request as redirect_uri pre-registered at the Authorization
* Server.
*/
let redirect_uri!: string
// End of prerequisites
const as = await oauth
.discoveryRequest(issuer, { algorithm })
.then((response) => oauth.processDiscoveryResponse(issuer, response))
const client: oauth.Client = {
client_id,
client_secret,
token_endpoint_auth_method: 'client_secret_basic',
}
const code_challenge_method = 'S256'
/**
* The following MUST be generated for every redirect to the authorization_endpoint. You must store
* the code_verifier and nonce in the end-user session such that it can be recovered as the user
* gets redirected from the authorization server back to your application.
*/
const code_verifier = oauth.generateRandomCodeVerifier()
const code_challenge = await oauth.calculatePKCECodeChallenge(code_verifier)
let state: string | undefined
// Pushed Authorization Request & Response (PAR)
let request_uri: string
{
const params = new URLSearchParams()
params.set('client_id', client.client_id)
params.set('redirect_uri', redirect_uri)
params.set('response_type', 'code')
params.set('scope', 'api:read')
params.set('code_challenge', code_challenge)
params.set('code_challenge_method', code_challenge_method)
/**
* We cannot be sure the AS 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 is why we're using it regardless.
*/
if (as.code_challenge_methods_supported?.includes('S256') !== true) {
state = oauth.generateRandomState()
params.set('state', state)
}
const response = await oauth.pushedAuthorizationRequest(as, client, params)
let challenges: oauth.WWWAuthenticateChallenge[] | undefined
if ((challenges = oauth.parseWwwAuthenticateChallenges(response))) {
for (const challenge of challenges) {
console.error('WWW-Authenticate Challenge', challenge)
}
throw new Error() // Handle WWW-Authenticate Challenges as needed
}
const result = await oauth.processPushedAuthorizationResponse(as, client, response)
if (oauth.isOAuth2Error(result)) {
console.error('Error Response', result)
throw new Error() // Handle OAuth 2.0 response body error
}
;({ request_uri } = result)
}
{
// redirect user to as.authorization_endpoint
const authorizationUrl = new URL(as.authorization_endpoint!)
authorizationUrl.searchParams.set('client_id', client.client_id)
authorizationUrl.searchParams.set('request_uri', request_uri)
// now redirect the user to authorizationUrl.href
}
// one eternity later, the user lands back on the redirect_uri
// Authorization Code Grant Request & Response
let access_token: string
{
// @ts-expect-error
const currentUrl: URL = getCurrentUrl()
const params = oauth.validateAuthResponse(as, client, currentUrl, state)
if (oauth.isOAuth2Error(params)) {
console.error('Error Response', params)
throw new Error() // Handle OAuth 2.0 redirect error
}
const response = await oauth.authorizationCodeGrantRequest(
as,
client,
params,
redirect_uri,
code_verifier,
)
let challenges: oauth.WWWAuthenticateChallenge[] | undefined
if ((challenges = oauth.parseWwwAuthenticateChallenges(response))) {
for (const challenge of challenges) {
console.error('WWW-Authenticate Challenge', challenge)
}
throw new Error() // Handle WWW-Authenticate Challenges as needed
}
const result = await oauth.processAuthorizationCodeOAuth2Response(as, client, response)
if (oauth.isOAuth2Error(result)) {
console.error('Error Response', result)
throw new Error() // Handle OAuth 2.0 response body error
}
console.log('Access Token Response', result)
;({ access_token } = result)
}
// Protected Resource Request
{
const response = await oauth.protectedResourceRequest(
access_token,
'GET',
new URL('https://rs.example.com/api'),
)
let challenges: oauth.WWWAuthenticateChallenge[] | undefined
if ((challenges = oauth.parseWwwAuthenticateChallenges(response))) {
for (const challenge of challenges) {
console.error('WWW-Authenticate Challenge', challenge)
}
throw new Error() // Handle WWW-Authenticate Challenges as needed
}
console.log('Protected Resource Response', await response.json())
}