@atproto/oauth-provider 0.2.0 → 0.2.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +42 -0
- package/dist/account/account-store.d.ts +2 -2
- package/dist/assets/app/bundle-manifest.json +3 -3
- package/dist/assets/app/main.css +1 -1
- package/dist/assets/app/main.js +3 -3
- package/dist/assets/app/main.js.map +1 -1
- package/dist/assets/assets-middleware.d.ts.map +1 -1
- package/dist/assets/assets-middleware.js +4 -2
- package/dist/assets/assets-middleware.js.map +1 -1
- package/dist/client/client-manager.d.ts.map +1 -1
- package/dist/client/client-manager.js +127 -118
- package/dist/client/client-manager.js.map +1 -1
- package/dist/client/client-utils.d.ts +1 -2
- package/dist/client/client-utils.d.ts.map +1 -1
- package/dist/client/client-utils.js +3 -12
- package/dist/client/client-utils.js.map +1 -1
- package/dist/client/client.d.ts +8 -3
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +70 -1
- package/dist/client/client.js.map +1 -1
- package/dist/constants.d.ts +0 -1
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +1 -2
- package/dist/constants.js.map +1 -1
- package/dist/errors/access-denied-error.d.ts +4 -4
- package/dist/errors/access-denied-error.d.ts.map +1 -1
- package/dist/errors/access-denied-error.js +2 -2
- package/dist/errors/access-denied-error.js.map +1 -1
- package/dist/errors/account-selection-required-error.d.ts +2 -2
- package/dist/errors/account-selection-required-error.d.ts.map +1 -1
- package/dist/errors/account-selection-required-error.js.map +1 -1
- package/dist/errors/consent-required-error.d.ts +2 -2
- package/dist/errors/consent-required-error.d.ts.map +1 -1
- package/dist/errors/consent-required-error.js.map +1 -1
- package/dist/errors/invalid-authorization-details-error.d.ts +2 -2
- package/dist/errors/invalid-authorization-details-error.d.ts.map +1 -1
- package/dist/errors/invalid-authorization-details-error.js.map +1 -1
- package/dist/errors/invalid-client-id-error.d.ts +1 -1
- package/dist/errors/invalid-client-id-error.d.ts.map +1 -1
- package/dist/errors/invalid-client-id-error.js +12 -6
- package/dist/errors/invalid-client-id-error.js.map +1 -1
- package/dist/errors/invalid-client-metadata-error.d.ts +1 -1
- package/dist/errors/invalid-client-metadata-error.d.ts.map +1 -1
- package/dist/errors/invalid-client-metadata-error.js +11 -3
- package/dist/errors/invalid-client-metadata-error.js.map +1 -1
- package/dist/errors/invalid-parameters-error.d.ts +2 -2
- package/dist/errors/invalid-parameters-error.d.ts.map +1 -1
- package/dist/errors/invalid-parameters-error.js.map +1 -1
- package/dist/errors/invalid-scope-error.d.ts +9 -0
- package/dist/errors/invalid-scope-error.d.ts.map +1 -0
- package/dist/errors/invalid-scope-error.js +14 -0
- package/dist/errors/invalid-scope-error.js.map +1 -0
- package/dist/errors/login-required-error.d.ts +2 -2
- package/dist/errors/login-required-error.d.ts.map +1 -1
- package/dist/errors/login-required-error.js.map +1 -1
- package/dist/lib/html/html.d.ts +1 -1
- package/dist/lib/html/html.d.ts.map +1 -1
- package/dist/lib/html/html.js +14 -11
- package/dist/lib/html/html.js.map +1 -1
- package/dist/lib/http/parser.d.ts +9 -2
- package/dist/lib/http/parser.d.ts.map +1 -1
- package/dist/lib/http/parser.js +15 -7
- package/dist/lib/http/parser.js.map +1 -1
- package/dist/lib/http/request.d.ts +0 -23
- package/dist/lib/http/request.d.ts.map +1 -1
- package/dist/lib/http/request.js +1 -11
- package/dist/lib/http/request.js.map +1 -1
- package/dist/lib/http/stream.d.ts +28 -6
- package/dist/lib/http/stream.d.ts.map +1 -1
- package/dist/lib/http/stream.js +21 -32
- package/dist/lib/http/stream.js.map +1 -1
- package/dist/lib/util/authorization-header.d.ts.map +1 -1
- package/dist/lib/util/authorization-header.js +1 -1
- package/dist/lib/util/authorization-header.js.map +1 -1
- package/dist/lib/util/hostname.d.ts +3 -2
- package/dist/lib/util/hostname.d.ts.map +1 -1
- package/dist/lib/util/hostname.js +12 -8
- package/dist/lib/util/hostname.js.map +1 -1
- package/dist/metadata/build-metadata.d.ts.map +1 -1
- package/dist/metadata/build-metadata.js +2 -1
- package/dist/metadata/build-metadata.js.map +1 -1
- package/dist/oauth-errors.d.ts +1 -0
- package/dist/oauth-errors.d.ts.map +1 -1
- package/dist/oauth-errors.js +3 -1
- package/dist/oauth-errors.js.map +1 -1
- package/dist/oauth-hooks.d.ts +3 -3
- package/dist/oauth-hooks.d.ts.map +1 -1
- package/dist/oauth-provider.d.ts +20 -22
- package/dist/oauth-provider.d.ts.map +1 -1
- package/dist/oauth-provider.js +234 -176
- package/dist/oauth-provider.js.map +1 -1
- package/dist/oauth-verifier.d.ts +2 -2
- package/dist/oauth-verifier.d.ts.map +1 -1
- package/dist/oauth-verifier.js.map +1 -1
- package/dist/output/build-authorize-data.d.ts +2 -2
- package/dist/output/build-authorize-data.d.ts.map +1 -1
- package/dist/output/send-authorize-redirect.d.ts +2 -4
- package/dist/output/send-authorize-redirect.d.ts.map +1 -1
- package/dist/output/send-authorize-redirect.js +5 -2
- package/dist/output/send-authorize-redirect.js.map +1 -1
- package/dist/request/request-data.d.ts +2 -2
- package/dist/request/request-data.d.ts.map +1 -1
- package/dist/request/request-info.d.ts +2 -2
- package/dist/request/request-info.d.ts.map +1 -1
- package/dist/request/request-manager.d.ts +4 -4
- package/dist/request/request-manager.d.ts.map +1 -1
- package/dist/request/request-manager.js +94 -60
- package/dist/request/request-manager.js.map +1 -1
- package/dist/signer/signed-token-payload.d.ts +122 -122
- package/dist/signer/signer.d.ts +41 -40
- package/dist/signer/signer.d.ts.map +1 -1
- package/dist/signer/signer.js +13 -15
- package/dist/signer/signer.js.map +1 -1
- package/dist/token/token-claims.d.ts +121 -121
- package/dist/token/token-data.d.ts +3 -3
- package/dist/token/token-data.d.ts.map +1 -1
- package/dist/token/token-manager.d.ts +4 -5
- package/dist/token/token-manager.d.ts.map +1 -1
- package/dist/token/token-manager.js +96 -72
- package/dist/token/token-manager.js.map +1 -1
- package/dist/token/verify-token-claims.d.ts +3 -3
- package/dist/token/verify-token-claims.d.ts.map +1 -1
- package/dist/token/verify-token-claims.js.map +1 -1
- package/package.json +7 -6
- package/src/assets/app/components/sign-in-form.tsx +31 -2
- package/src/assets/app/components/url-viewer.tsx +3 -3
- package/src/assets/assets-middleware.ts +4 -2
- package/src/client/client-manager.ts +163 -161
- package/src/client/client-utils.ts +7 -12
- package/src/client/client.ts +112 -3
- package/src/constants.ts +0 -2
- package/src/errors/access-denied-error.ts +10 -4
- package/src/errors/account-selection-required-error.ts +2 -2
- package/src/errors/consent-required-error.ts +2 -2
- package/src/errors/invalid-authorization-details-error.ts +2 -2
- package/src/errors/invalid-client-id-error.ts +15 -4
- package/src/errors/invalid-client-metadata-error.ts +15 -3
- package/src/errors/invalid-parameters-error.ts +2 -2
- package/src/errors/invalid-scope-error.ts +15 -0
- package/src/errors/login-required-error.ts +2 -2
- package/src/lib/html/html.ts +14 -12
- package/src/lib/http/parser.ts +21 -8
- package/src/lib/http/request.ts +1 -23
- package/src/lib/http/stream.ts +29 -60
- package/src/lib/util/authorization-header.ts +5 -2
- package/src/lib/util/hostname.ts +9 -5
- package/src/metadata/build-metadata.ts +3 -1
- package/src/oauth-errors.ts +1 -0
- package/src/oauth-hooks.ts +3 -3
- package/src/oauth-provider.ts +368 -269
- package/src/oauth-verifier.ts +2 -2
- package/src/output/build-authorize-data.ts +2 -2
- package/src/output/send-authorize-redirect.ts +7 -6
- package/src/request/request-data.ts +2 -2
- package/src/request/request-info.ts +2 -2
- package/src/request/request-manager.ts +129 -103
- package/src/signer/signer.ts +24 -25
- package/src/token/token-data.ts +3 -3
- package/src/token/token-manager.ts +141 -99
- package/src/token/verify-token-claims.ts +3 -3
- package/dist/request/types.d.ts +0 -328
- package/dist/request/types.d.ts.map +0 -1
- package/dist/request/types.js +0 -27
- package/dist/request/types.js.map +0 -1
- package/dist/token/types.d.ts +0 -250
- package/dist/token/types.d.ts.map +0 -1
- package/dist/token/types.js +0 -36
- package/dist/token/types.js.map +0 -1
- package/src/request/types.ts +0 -48
- package/src/token/types.ts +0 -86
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { isSignedJwt } from '@atproto/jwk'
|
|
2
2
|
import {
|
|
3
|
-
AccessToken,
|
|
4
3
|
CLIENT_ASSERTION_TYPE_JWT_BEARER,
|
|
5
|
-
|
|
4
|
+
OAuthAccessToken,
|
|
5
|
+
OAuthAuthorizationRequestParameters,
|
|
6
|
+
OAuthAuthorizationCodeGrantTokenRequest,
|
|
7
|
+
OAuthClientCredentialsGrantTokenRequest,
|
|
8
|
+
OAuthPasswordGrantTokenRequest,
|
|
9
|
+
OAuthRefreshTokenGrantTokenRequest,
|
|
6
10
|
OAuthTokenResponse,
|
|
7
11
|
OAuthTokenType,
|
|
8
12
|
} from '@atproto/oauth-types'
|
|
@@ -27,11 +31,14 @@ import { InvalidGrantError } from '../errors/invalid-grant-error.js'
|
|
|
27
31
|
import { InvalidRequestError } from '../errors/invalid-request-error.js'
|
|
28
32
|
import { InvalidTokenError } from '../errors/invalid-token-error.js'
|
|
29
33
|
import { dateToEpoch, dateToRelativeSeconds } from '../lib/util/date.js'
|
|
30
|
-
import { compareRedirectUri } from '../lib/util/redirect-uri.js'
|
|
31
34
|
import { OAuthHooks } from '../oauth-hooks.js'
|
|
32
|
-
import { isCode } from '../request/code.js'
|
|
35
|
+
import { Code, isCode } from '../request/code.js'
|
|
33
36
|
import { Signer } from '../signer/signer.js'
|
|
34
|
-
import {
|
|
37
|
+
import {
|
|
38
|
+
generateRefreshToken,
|
|
39
|
+
isRefreshToken,
|
|
40
|
+
refreshTokenSchema,
|
|
41
|
+
} from './refresh-token.js'
|
|
35
42
|
import { TokenClaims } from './token-claims.js'
|
|
36
43
|
import { TokenData } from './token-data.js'
|
|
37
44
|
import {
|
|
@@ -41,7 +48,6 @@ import {
|
|
|
41
48
|
tokenIdSchema,
|
|
42
49
|
} from './token-id.js'
|
|
43
50
|
import { TokenInfo, TokenStore } from './token-store.js'
|
|
44
|
-
import { CodeGrantRequest, RefreshGrantRequest } from './types.js'
|
|
45
51
|
import {
|
|
46
52
|
VerifyTokenClaimsOptions,
|
|
47
53
|
VerifyTokenClaimsResult,
|
|
@@ -78,18 +84,25 @@ export class TokenManager {
|
|
|
78
84
|
clientAuth: ClientAuth,
|
|
79
85
|
account: Account,
|
|
80
86
|
device: null | { id: DeviceId; info: DeviceAccountInfo },
|
|
81
|
-
parameters:
|
|
82
|
-
input:
|
|
87
|
+
parameters: OAuthAuthorizationRequestParameters,
|
|
88
|
+
input:
|
|
89
|
+
| OAuthAuthorizationCodeGrantTokenRequest
|
|
90
|
+
| OAuthClientCredentialsGrantTokenRequest
|
|
91
|
+
| OAuthPasswordGrantTokenRequest,
|
|
83
92
|
dpopJkt: null | string,
|
|
84
93
|
): Promise<OAuthTokenResponse> {
|
|
94
|
+
// @NOTE the atproto specific DPoP requirement is enforced though the
|
|
95
|
+
// "dpop_bound_access_tokens" metadata, which is enforced by the
|
|
96
|
+
// ClientManager class.
|
|
85
97
|
if (client.metadata.dpop_bound_access_tokens && !dpopJkt) {
|
|
86
98
|
throw new InvalidDpopProofError('DPoP proof required')
|
|
87
99
|
}
|
|
88
100
|
|
|
89
101
|
if (!parameters.dpop_jkt) {
|
|
102
|
+
// Allow clients to bind their access tokens to a DPoP key during
|
|
103
|
+
// token request if they didn't provide a "dpop_jkt" during the
|
|
104
|
+
// authorization request.
|
|
90
105
|
if (dpopJkt) parameters = { ...parameters, dpop_jkt: dpopJkt }
|
|
91
|
-
} else if (!dpopJkt) {
|
|
92
|
-
throw new InvalidDpopProofError('DPoP proof required')
|
|
93
106
|
} else if (parameters.dpop_jkt !== dpopJkt) {
|
|
94
107
|
throw new InvalidDpopKeyBindingError()
|
|
95
108
|
}
|
|
@@ -109,78 +122,80 @@ export class TokenManager {
|
|
|
109
122
|
)
|
|
110
123
|
}
|
|
111
124
|
|
|
125
|
+
let code: Code | null = null
|
|
126
|
+
|
|
112
127
|
switch (input.grant_type) {
|
|
113
|
-
case 'authorization_code':
|
|
114
|
-
if (!
|
|
115
|
-
throw new InvalidGrantError('
|
|
128
|
+
case 'authorization_code': {
|
|
129
|
+
if (!isCode(input.code)) {
|
|
130
|
+
throw new InvalidGrantError('Invalid code')
|
|
116
131
|
}
|
|
117
132
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
)
|
|
122
|
-
if (redirect_uri) {
|
|
123
|
-
parameters = { ...parameters, redirect_uri }
|
|
124
|
-
} else {
|
|
125
|
-
throw new InvalidGrantError(`Invalid redirect_uri`)
|
|
126
|
-
}
|
|
127
|
-
} else if (parameters.redirect_uri !== input.redirect_uri) {
|
|
128
|
-
throw new InvalidGrantError(
|
|
129
|
-
'This code was issued for another redirect_uri',
|
|
130
|
-
)
|
|
133
|
+
const tokenInfo = await this.store.findTokenByCode(input.code)
|
|
134
|
+
if (tokenInfo) {
|
|
135
|
+
await this.store.deleteToken(tokenInfo.id)
|
|
136
|
+
throw new InvalidGrantError(`Code replayed`)
|
|
131
137
|
}
|
|
132
138
|
|
|
133
|
-
|
|
139
|
+
code = input.code
|
|
134
140
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
141
|
+
if (parameters.redirect_uri !== input.redirect_uri) {
|
|
142
|
+
throw new InvalidGrantError(
|
|
143
|
+
'The redirect_uri parameter must match the one used in the authorization request',
|
|
144
|
+
)
|
|
145
|
+
}
|
|
138
146
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
}
|
|
143
|
-
// Prevent client from generating too short code_verifiers
|
|
144
|
-
if (input.code_verifier.length < 43) {
|
|
145
|
-
throw new InvalidGrantError('code_verifier too short')
|
|
146
|
-
}
|
|
147
|
-
switch (parameters.code_challenge_method) {
|
|
148
|
-
case undefined: // Default is "plain" (per spec)
|
|
149
|
-
case 'plain': {
|
|
150
|
-
if (parameters.code_challenge !== input.code_verifier) {
|
|
151
|
-
throw new InvalidGrantError('Invalid code_verifier')
|
|
147
|
+
if (parameters.code_challenge) {
|
|
148
|
+
if (!input.code_verifier) {
|
|
149
|
+
throw new InvalidGrantError('code_verifier is required')
|
|
152
150
|
}
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
case 'S256': {
|
|
156
|
-
// Because the code_challenge is base64url-encoded, we will decode
|
|
157
|
-
// it in order to compare based on bytes.
|
|
158
|
-
const inputChallenge = Buffer.from(
|
|
159
|
-
parameters.code_challenge,
|
|
160
|
-
'base64',
|
|
161
|
-
)
|
|
162
|
-
const computedChallenge = createHash('sha256')
|
|
163
|
-
.update(input.code_verifier)
|
|
164
|
-
.digest()
|
|
165
|
-
if (inputChallenge.compare(computedChallenge) !== 0) {
|
|
166
|
-
throw new InvalidGrantError('Invalid code_verifier')
|
|
151
|
+
if (input.code_verifier.length < 43) {
|
|
152
|
+
throw new InvalidGrantError('code_verifier too short')
|
|
167
153
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
154
|
+
switch (parameters.code_challenge_method ?? 'plain') {
|
|
155
|
+
case 'plain': {
|
|
156
|
+
if (parameters.code_challenge !== input.code_verifier) {
|
|
157
|
+
throw new InvalidGrantError('Invalid code_verifier')
|
|
158
|
+
}
|
|
159
|
+
break
|
|
160
|
+
}
|
|
161
|
+
case 'S256': {
|
|
162
|
+
const inputChallenge = Buffer.from(
|
|
163
|
+
parameters.code_challenge,
|
|
164
|
+
'base64',
|
|
165
|
+
)
|
|
166
|
+
const computedChallenge = createHash('sha256')
|
|
167
|
+
.update(input.code_verifier)
|
|
168
|
+
.digest()
|
|
169
|
+
if (inputChallenge.compare(computedChallenge) !== 0) {
|
|
170
|
+
throw new InvalidGrantError('Invalid code_verifier')
|
|
171
|
+
}
|
|
172
|
+
break
|
|
173
|
+
}
|
|
174
|
+
default: {
|
|
175
|
+
// Should never happen (because request validation should catch this)
|
|
176
|
+
throw new Error(`Unsupported code_challenge_method`)
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
} else if (input.code_verifier !== undefined) {
|
|
171
180
|
throw new InvalidRequestError(
|
|
172
|
-
|
|
181
|
+
"code_challenge parameter wasn't provided",
|
|
173
182
|
)
|
|
174
183
|
}
|
|
184
|
+
|
|
185
|
+
if (!device) {
|
|
186
|
+
// Fool-proofing (authorization_code grant should always have a device)
|
|
187
|
+
throw new InvalidRequestError('consent was not given for this device')
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
break
|
|
175
191
|
}
|
|
176
|
-
}
|
|
177
192
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
193
|
+
default: {
|
|
194
|
+
// Other grants (e.g "password", "client_credentials") could be added
|
|
195
|
+
// here in the future...
|
|
196
|
+
throw new InvalidRequestError(
|
|
197
|
+
`Unsupported grant type "${input.grant_type}"`,
|
|
198
|
+
)
|
|
184
199
|
}
|
|
185
200
|
}
|
|
186
201
|
|
|
@@ -207,41 +222,50 @@ export class TokenManager {
|
|
|
207
222
|
sub: account.sub,
|
|
208
223
|
parameters,
|
|
209
224
|
details: authorizationDetails ?? null,
|
|
210
|
-
code
|
|
225
|
+
code,
|
|
211
226
|
}
|
|
212
227
|
|
|
213
228
|
await this.store.createToken(tokenId, tokenData, refreshToken)
|
|
214
229
|
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
230
|
+
try {
|
|
231
|
+
const accessToken: OAuthAccessToken = !this.useJwtAccessToken(account)
|
|
232
|
+
? tokenId
|
|
233
|
+
: await this.signer.accessToken(client, parameters, {
|
|
234
|
+
// We don't specify the alg here. We suppose the Resource server will be
|
|
235
|
+
// able to verify the token using any alg.
|
|
236
|
+
aud: account.aud,
|
|
237
|
+
sub: account.sub,
|
|
238
|
+
alg: undefined,
|
|
239
|
+
exp: expiresAt,
|
|
240
|
+
iat: now,
|
|
241
|
+
jti: tokenId,
|
|
242
|
+
cnf: parameters.dpop_jkt ? { jkt: parameters.dpop_jkt } : undefined,
|
|
243
|
+
authorization_details: authorizationDetails,
|
|
244
|
+
})
|
|
227
245
|
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
246
|
+
return this.buildTokenResponse(
|
|
247
|
+
client,
|
|
248
|
+
accessToken,
|
|
249
|
+
refreshToken,
|
|
250
|
+
expiresAt,
|
|
251
|
+
parameters,
|
|
252
|
+
account,
|
|
253
|
+
authorizationDetails,
|
|
254
|
+
)
|
|
255
|
+
} catch (err) {
|
|
256
|
+
// Just in case the token could not be issued, we delete it from the store
|
|
257
|
+
await this.store.deleteToken(tokenId)
|
|
258
|
+
|
|
259
|
+
throw err
|
|
260
|
+
}
|
|
237
261
|
}
|
|
238
262
|
|
|
239
263
|
protected async buildTokenResponse(
|
|
240
264
|
client: Client,
|
|
241
|
-
accessToken:
|
|
265
|
+
accessToken: OAuthAccessToken,
|
|
242
266
|
refreshToken: string | undefined,
|
|
243
267
|
expiresAt: Date,
|
|
244
|
-
parameters:
|
|
268
|
+
parameters: OAuthAuthorizationRequestParameters,
|
|
245
269
|
account: Account,
|
|
246
270
|
authorizationDetails: null | any,
|
|
247
271
|
): Promise<OAuthTokenResponse> {
|
|
@@ -289,12 +313,16 @@ export class TokenManager {
|
|
|
289
313
|
async refresh(
|
|
290
314
|
client: Client,
|
|
291
315
|
clientAuth: ClientAuth,
|
|
292
|
-
input:
|
|
316
|
+
input: OAuthRefreshTokenGrantTokenRequest,
|
|
293
317
|
dpopJkt: null | string,
|
|
294
318
|
): Promise<OAuthTokenResponse> {
|
|
295
|
-
const
|
|
296
|
-
|
|
297
|
-
|
|
319
|
+
const refreshTokenParsed = refreshTokenSchema.safeParse(input.refresh_token)
|
|
320
|
+
if (!refreshTokenParsed.success) {
|
|
321
|
+
throw new InvalidRequestError('Invalid refresh token')
|
|
322
|
+
}
|
|
323
|
+
const refreshToken = refreshTokenParsed.data
|
|
324
|
+
|
|
325
|
+
const tokenInfo = await this.store.findTokenByRefreshToken(refreshToken)
|
|
298
326
|
if (!tokenInfo?.currentRefreshToken) {
|
|
299
327
|
throw new InvalidGrantError(`Invalid refresh token`)
|
|
300
328
|
}
|
|
@@ -303,12 +331,24 @@ export class TokenManager {
|
|
|
303
331
|
const { parameters } = data
|
|
304
332
|
|
|
305
333
|
try {
|
|
306
|
-
if (tokenInfo.currentRefreshToken !==
|
|
334
|
+
if (tokenInfo.currentRefreshToken !== refreshToken) {
|
|
307
335
|
throw new InvalidGrantError(`refresh token replayed`)
|
|
308
336
|
}
|
|
309
337
|
|
|
310
338
|
await this.validateAccess(client, clientAuth, tokenInfo)
|
|
311
339
|
|
|
340
|
+
if (input.grant_type !== 'refresh_token') {
|
|
341
|
+
// Fool-proofing (should never happen)
|
|
342
|
+
throw new InvalidGrantError(`Invalid grant type`)
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
if (!client.metadata.grant_types.includes(input.grant_type)) {
|
|
346
|
+
// In case the client metadata was updated after the token was issued
|
|
347
|
+
throw new InvalidGrantError(
|
|
348
|
+
`This client is not allowed to use the "${input.grant_type}" grant type`,
|
|
349
|
+
)
|
|
350
|
+
}
|
|
351
|
+
|
|
312
352
|
if (parameters.dpop_jkt) {
|
|
313
353
|
if (!dpopJkt) {
|
|
314
354
|
throw new InvalidDpopProofError('DPoP proof required')
|
|
@@ -370,11 +410,13 @@ export class TokenManager {
|
|
|
370
410
|
},
|
|
371
411
|
)
|
|
372
412
|
|
|
373
|
-
const accessToken:
|
|
413
|
+
const accessToken: OAuthAccessToken = !this.useJwtAccessToken(account)
|
|
374
414
|
? nextTokenId
|
|
375
|
-
: await this.signer.accessToken(client, parameters,
|
|
415
|
+
: await this.signer.accessToken(client, parameters, {
|
|
376
416
|
// We don't specify the alg here. We suppose the Resource server will be
|
|
377
417
|
// able to verify the token using any alg.
|
|
418
|
+
aud: account.aud,
|
|
419
|
+
sub: account.sub,
|
|
378
420
|
alg: undefined,
|
|
379
421
|
exp: expiresAt,
|
|
380
422
|
iat: now,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { OAuthAccessToken, OAuthTokenType } from '@atproto/oauth-types'
|
|
2
2
|
|
|
3
3
|
import { InvalidDpopKeyBindingError } from '../errors/invalid-dpop-key-binding-error.js'
|
|
4
4
|
import { InvalidDpopProofError } from '../errors/invalid-dpop-proof-error.js'
|
|
@@ -15,14 +15,14 @@ export type VerifyTokenClaimsOptions = {
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
export type VerifyTokenClaimsResult = {
|
|
18
|
-
token:
|
|
18
|
+
token: OAuthAccessToken
|
|
19
19
|
tokenId: TokenId
|
|
20
20
|
tokenType: OAuthTokenType
|
|
21
21
|
claims: TokenClaims
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export function verifyTokenClaims(
|
|
25
|
-
token:
|
|
25
|
+
token: OAuthAccessToken,
|
|
26
26
|
tokenId: TokenId,
|
|
27
27
|
tokenType: OAuthTokenType,
|
|
28
28
|
dpopJkt: string | null,
|