@udibo/oauth2 0.4.1 → 0.5.1
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/README.md +3 -3
- package/SECURITY.md +0 -4
- package/esm/adapters/hono/_common.d.ts +10 -13
- package/esm/adapters/hono/_common.js +11 -20
- package/esm/adapters/hono/authorization-server.d.ts +16 -10
- package/esm/adapters/hono/authorization-server.js +13 -8
- package/esm/adapters/hono/bff/auth-request-store.d.ts +4 -0
- package/esm/adapters/hono/bff/auth-request-store.js +4 -0
- package/esm/adapters/hono/bff/bff.d.ts +29 -18
- package/esm/adapters/hono/bff/bff.js +15 -11
- package/esm/adapters/hono/bff/proxy.js +7 -55
- package/esm/adapters/hono/bff/session-store.d.ts +9 -9
- package/esm/adapters/hono/bff/session-store.js +3 -2
- package/esm/adapters/hono/bff/testing.d.ts +14 -3
- package/esm/adapters/hono/bff/testing.js +14 -3
- package/esm/adapters/hono/identity.d.ts +5 -3
- package/esm/adapters/hono/identity.js +1 -17
- package/esm/adapters/hono/resource-server.d.ts +3 -2
- package/esm/adapters/hono/resource-server.js +3 -2
- package/esm/client/_http.d.ts +4 -7
- package/esm/client/_http.js +4 -37
- package/esm/client/base.d.ts +3 -6
- package/esm/client/bff-client.d.ts +7 -6
- package/esm/client/bff-client.js +7 -6
- package/esm/client/browser-storage.d.ts +33 -34
- package/esm/client/browser-storage.js +47 -32
- package/esm/client/check.d.ts +10 -3
- package/esm/client/check.js +28 -8
- package/esm/client/direct-client.d.ts +49 -29
- package/esm/client/direct-client.js +59 -59
- package/esm/client/discovery-cache.d.ts +12 -5
- package/esm/client/discovery-cache.js +11 -4
- package/esm/client/events.d.ts +11 -3
- package/esm/client/events.js +1 -1
- package/esm/client/mod.d.ts +6 -4
- package/esm/client/mod.js +5 -3
- package/esm/client/storage.d.ts +53 -23
- package/esm/client/storage.js +43 -15
- package/esm/errors.d.ts +15 -12
- package/esm/errors.js +12 -8
- package/esm/identity/delivery.d.ts +7 -7
- package/esm/identity/delivery.js +1 -2
- package/esm/identity/errors.d.ts +10 -4
- package/esm/identity/errors.js +4 -1
- package/esm/identity/events.d.ts +21 -5
- package/esm/identity/events.js +4 -2
- package/esm/identity/external/_shared.d.ts +5 -7
- package/esm/identity/external/_shared.js +5 -7
- package/esm/identity/external/_token-exchange.d.ts +3 -6
- package/esm/identity/external/_token-exchange.js +2 -5
- package/esm/identity/external/apple-client-secret.d.ts +13 -8
- package/esm/identity/external/apple-client-secret.js +9 -6
- package/esm/identity/external/apple.d.ts +17 -10
- package/esm/identity/external/apple.js +12 -9
- package/esm/identity/external/discord.d.ts +5 -1
- package/esm/identity/external/errors.d.ts +6 -2
- package/esm/identity/external/errors.js +6 -2
- package/esm/identity/external/flow.d.ts +9 -6
- package/esm/identity/external/flow.js +6 -5
- package/esm/identity/external/github.d.ts +7 -3
- package/esm/identity/external/github.js +2 -2
- package/esm/identity/external/google.d.ts +5 -1
- package/esm/identity/external/oauth2.d.ts +13 -2
- package/esm/identity/external/oauth2.js +3 -3
- package/esm/identity/external/oidc.d.ts +21 -10
- package/esm/identity/external/oidc.js +11 -6
- package/esm/identity/external/provider.d.ts +4 -2
- package/esm/identity/identifier.d.ts +6 -5
- package/esm/identity/identifier.js +6 -5
- package/esm/identity/lockout.d.ts +4 -3
- package/esm/identity/lockout.js +4 -3
- package/esm/identity/mfa/mod.d.ts +2 -1
- package/esm/identity/mfa/mod.js +2 -1
- package/esm/identity/mfa/recovery-codes.d.ts +2 -1
- package/esm/identity/mfa/service.d.ts +29 -11
- package/esm/identity/mfa/service.js +35 -11
- package/esm/identity/mfa/totp.d.ts +4 -2
- package/esm/identity/mfa/totp.js +4 -2
- package/esm/identity/migration.d.ts +8 -7
- package/esm/identity/migration.js +9 -12
- package/esm/identity/mod.d.ts +2 -2
- package/esm/identity/mod.js +2 -2
- package/esm/identity/otp.d.ts +23 -16
- package/esm/identity/otp.js +13 -12
- package/esm/identity/password-policy.d.ts +3 -3
- package/esm/identity/password-policy.js +3 -3
- package/esm/identity/password.d.ts +23 -20
- package/esm/identity/password.js +15 -16
- package/esm/identity/rate-limit.d.ts +1 -7
- package/esm/identity/rate-limit.js +1 -2
- package/esm/identity/service.d.ts +76 -44
- package/esm/identity/service.js +86 -60
- package/esm/identity/session.d.ts +5 -4
- package/esm/identity/session.js +5 -4
- package/esm/identity/token-flow.d.ts +8 -8
- package/esm/identity/token-flow.js +5 -6
- package/esm/models/client.d.ts +7 -4
- package/esm/models/token.d.ts +2 -3
- package/esm/react/callback.d.ts +3 -3
- package/esm/react/callback.js +3 -3
- package/esm/react/components/class-names.d.ts +3 -6
- package/esm/react/components/class-names.js +3 -6
- package/esm/react/components/internal.d.ts +7 -17
- package/esm/react/components/internal.js +7 -17
- package/esm/react/components/mod.d.ts +7 -5
- package/esm/react/components/mod.js +7 -5
- package/esm/react/components/types.d.ts +8 -6
- package/esm/react/context.d.ts +3 -3
- package/esm/react/provider.d.ts +3 -2
- package/esm/react/provider.js +0 -13
- package/esm/react/require-auth.d.ts +0 -3
- package/esm/react/require-auth.js +0 -3
- package/esm/react/testing.d.ts +3 -2
- package/esm/react/testing.js +3 -3
- package/esm/react/use-oauth2.d.ts +1 -1
- package/esm/server/authorization-server.d.ts +81 -54
- package/esm/server/authorization-server.js +76 -100
- package/esm/server/grants/authorization-code.d.ts +7 -3
- package/esm/server/grants/authorization-code.js +6 -2
- package/esm/server/grants/device-authorization.d.ts +1 -1
- package/esm/server/grants/grant.d.ts +15 -8
- package/esm/server/grants/grant.js +26 -18
- package/esm/server/grants/password.d.ts +1 -1
- package/esm/server/grants/refresh-token.d.ts +6 -3
- package/esm/server/grants/refresh-token.js +4 -3
- package/esm/server/introspection-token-reader.d.ts +16 -7
- package/esm/server/introspection-token-reader.js +9 -3
- package/esm/server/jwks-token-reader.d.ts +3 -3
- package/esm/server/jwks-token-reader.js +3 -3
- package/esm/server/redirect-uri.d.ts +6 -9
- package/esm/server/redirect-uri.js +7 -34
- package/esm/server/resource-server.d.ts +5 -3
- package/esm/server/resource-server.js +5 -3
- package/esm/server/services/client.d.ts +5 -1
- package/esm/server/services/device-authorization.d.ts +6 -6
- package/esm/server/services/token.d.ts +15 -8
- package/esm/server/services/token.js +1 -1
- package/esm/server/signing-keys.d.ts +24 -9
- package/esm/server/signing-keys.js +20 -9
- package/esm/testing/contract/mod.d.ts +3 -3
- package/esm/testing/contract/mod.js +3 -3
- package/esm/testing/contract/tenant.d.ts +1 -1
- package/esm/testing/contract/tenant.js +2 -2
- package/esm/testing/mod.d.ts +6 -4
- package/esm/testing/mod.js +6 -4
- package/esm/testing/services.d.ts +1 -1
- package/esm/testing/services.js +1 -1
- package/esm/testing/tenant.d.ts +14 -5
- package/esm/utils/_buffer.d.ts +3 -2
- package/esm/utils/_buffer.js +3 -2
- package/esm/utils/_timing-safe.d.ts +8 -6
- package/esm/utils/_timing-safe.js +8 -6
- package/esm/utils/basic-auth.d.ts +5 -12
- package/esm/utils/basic-auth.js +5 -12
- package/esm/utils/crypto.d.ts +2 -1
- package/esm/utils/crypto.js +2 -1
- package/esm/utils/text.d.ts +2 -9
- package/esm/utils/text.js +2 -9
- package/esm/utils/url.d.ts +2 -2
- package/esm/utils/url.js +0 -2
- package/package.json +1 -1
|
@@ -106,7 +106,7 @@ const AUTHORIZATION_CODE_GRANT_TYPE = "authorization_code";
|
|
|
106
106
|
* these paths — as the Hono adapter's `routes()` does — to match what the
|
|
107
107
|
* server advertises in its RFC 8414 metadata.
|
|
108
108
|
*
|
|
109
|
-
* Covers the whole served surface: the
|
|
109
|
+
* Covers the whole served surface: the eight endpoints the server can advertise,
|
|
110
110
|
* plus the two discovery documents (`metadata` for RFC 8414,
|
|
111
111
|
* `oidcMetadata` for OIDC Discovery) that are served but never advertised.
|
|
112
112
|
* Pair it with {@link ENDPOINT_METHODS} to enumerate path *and* verbs.
|
|
@@ -123,7 +123,7 @@ export const ENDPOINT_PATHS = Object
|
|
|
123
123
|
/**
|
|
124
124
|
* The HTTP methods each endpoint in {@link ENDPOINT_PATHS} answers — `POST` for
|
|
125
125
|
* the token-style endpoints, `GET` for authorize/JWKS/discovery, both for
|
|
126
|
-
* UserInfo. Read it instead of re-deriving verbs per endpoint, so a hand-rolled
|
|
126
|
+
* UserInfo and end-session. Read it instead of re-deriving verbs per endpoint, so a hand-rolled
|
|
127
127
|
* mount answers exactly what the built-in `routes()` does.
|
|
128
128
|
*
|
|
129
129
|
* @example
|
|
@@ -148,6 +148,7 @@ function isDeviceAuthorizationGrant(grant) {
|
|
|
148
148
|
* - Introspection endpoint (POST /introspect) - RFC 7662
|
|
149
149
|
* - Device authorization endpoint (POST /device_authorization) - RFC 8628
|
|
150
150
|
* - Metadata endpoint (GET /.well-known/oauth-authorization-server) - RFC 8414
|
|
151
|
+
* - OIDC discovery, JWKS, UserInfo, and end-session endpoints, when configured
|
|
151
152
|
*
|
|
152
153
|
* Framework-agnostic: each `handle*Request` method takes a web `Request` and
|
|
153
154
|
* returns a `Response`, so an adapter routes its endpoints to them. Extends
|
|
@@ -189,7 +190,6 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
189
190
|
#userClaims;
|
|
190
191
|
#introspectionClaims;
|
|
191
192
|
#canIntrospectToken;
|
|
192
|
-
/** Resolves the per-request {@link AuthorizationServerContext}. */
|
|
193
193
|
#resolve;
|
|
194
194
|
/**
|
|
195
195
|
* Creates an authorization server from the given
|
|
@@ -203,6 +203,7 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
203
203
|
resolve: options.resolve,
|
|
204
204
|
Scope: options.Scope,
|
|
205
205
|
realm: options.realm,
|
|
206
|
+
clockSkewSeconds: options.clockSkewSeconds,
|
|
206
207
|
errorFormat: options.errorFormat,
|
|
207
208
|
throwOnError: options.throwOnError,
|
|
208
209
|
});
|
|
@@ -251,7 +252,6 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
251
252
|
async authorizationContext(request) {
|
|
252
253
|
return await this.#resolve(request);
|
|
253
254
|
}
|
|
254
|
-
/** Resolves the endpoint URLs for a request (issuer-derived unless set). */
|
|
255
255
|
#resolvedEndpoints(context) {
|
|
256
256
|
const { issuer } = context;
|
|
257
257
|
const endpoints = {};
|
|
@@ -309,22 +309,15 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
309
309
|
},
|
|
310
310
|
});
|
|
311
311
|
}
|
|
312
|
-
/**
|
|
313
|
-
* Applies `Cache-Control: no-store` and `Pragma: no-cache` headers to an
|
|
314
|
-
* error per RFC 6749 Section 5.2. The headers land on both the rethrown
|
|
315
|
-
* error (when {@link throwOnError} is true) and the generated response.
|
|
316
|
-
*/
|
|
312
|
+
/** Sets the RFC 6749 §5.2 `no-store` cache headers on an error. */
|
|
317
313
|
addCacheHeaders(error) {
|
|
318
314
|
error.headers.set("Cache-Control", "no-store");
|
|
319
315
|
error.headers.set("Pragma", "no-cache");
|
|
320
316
|
}
|
|
321
317
|
/**
|
|
322
|
-
* Error-prepare callback for
|
|
323
|
-
*
|
|
324
|
-
*
|
|
325
|
-
* attempted via the `Authorization` header and failed with a 401 — the
|
|
326
|
-
* matching `WWW-Authenticate: Basic` challenge that RFC 6749 Section 5.2
|
|
327
|
-
* requires.
|
|
318
|
+
* Error-prepare callback for client-authenticated endpoints: cache headers,
|
|
319
|
+
* plus the RFC 6749 §5.2 `WWW-Authenticate: Basic` challenge on a 401 when
|
|
320
|
+
* the request sent an `Authorization` header.
|
|
328
321
|
*/
|
|
329
322
|
prepareClientAuthError(request) {
|
|
330
323
|
return (error) => {
|
|
@@ -334,16 +327,11 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
334
327
|
}
|
|
335
328
|
};
|
|
336
329
|
}
|
|
337
|
-
/**
|
|
338
|
-
* Rejects a client that is not registered for a grant type
|
|
339
|
-
* (RFC 6749 Section 5.2 `unauthorized_client`).
|
|
340
|
-
*/
|
|
341
330
|
#assertGrantAuthorized(client, grantType) {
|
|
342
331
|
if (!client.grants?.includes(grantType)) {
|
|
343
332
|
throw new UnauthorizedClientError(`client is not authorized to use the ${grantType} grant type`);
|
|
344
333
|
}
|
|
345
334
|
}
|
|
346
|
-
/** The grant a token request's `grant_type` dispatches to. */
|
|
347
335
|
#grantFromBody(body) {
|
|
348
336
|
const grantType = body.get("grant_type");
|
|
349
337
|
if (typeof grantType !== "string") {
|
|
@@ -381,8 +369,10 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
381
369
|
* Supports multiple grant types based on the configured grants.
|
|
382
370
|
*
|
|
383
371
|
* A malformed request — wrong method, a content-type other than
|
|
384
|
-
* `application/x-www-form-urlencoded`, or
|
|
385
|
-
* `invalid_request`.
|
|
372
|
+
* `application/x-www-form-urlencoded`, or no `grant_type` — is a 400
|
|
373
|
+
* `invalid_request`. A `grant_type` with no registered grant is
|
|
374
|
+
* `unsupported_grant_type`, checked before client authentication; a client
|
|
375
|
+
* not registered for the grant is `unauthorized_client`.
|
|
386
376
|
*/
|
|
387
377
|
async handleTokenRequest(request) {
|
|
388
378
|
try {
|
|
@@ -401,6 +391,8 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
401
391
|
* Mint an OIDC id_token for a user-bound token whose scope includes
|
|
402
392
|
* `openid`, or `undefined` when the OIDC surface is off (no signing keys),
|
|
403
393
|
* the token has no user, the scope lacks `openid`, or no issuer resolved.
|
|
394
|
+
* The id_token's `aud` is the client id and it expires one hour after
|
|
395
|
+
* issue.
|
|
404
396
|
*/
|
|
405
397
|
async mintIdToken(token, context) {
|
|
406
398
|
if (!this.signingKeys || !token.user || !context.issuer)
|
|
@@ -418,9 +410,10 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
418
410
|
};
|
|
419
411
|
if (token.nonce)
|
|
420
412
|
claims.nonce = token.nonce;
|
|
413
|
+
else
|
|
414
|
+
delete claims.nonce;
|
|
421
415
|
return await signJwt(key, claims);
|
|
422
416
|
}
|
|
423
|
-
/** OIDC claims released for a user: `userClaims` under the winning `sub`. */
|
|
424
417
|
async #oidcClaims(user, scope, authenticationContext) {
|
|
425
418
|
return {
|
|
426
419
|
...(await this.#userClaims?.(user, scope, snapshotAuthenticationContext(authenticationContext)) ?? {}),
|
|
@@ -506,8 +499,12 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
506
499
|
* `endSession` does the ending and this method does the protocol around it:
|
|
507
500
|
* identifying the relying party, authorizing the return trip, and shaping
|
|
508
501
|
* the response. `endSession` is always called, even when nothing else about
|
|
509
|
-
* the
|
|
510
|
-
* the worst possible failure mode for this endpoint.
|
|
502
|
+
* the client, hint, or redirect checks out, because a logout that refuses to
|
|
503
|
+
* log anyone out is the worst possible failure mode for this endpoint. The
|
|
504
|
+
* sole exception is a `client_id` that is not among the audiences of a
|
|
505
|
+
* verified `id_token_hint` sent with it: OIDC RP-Initiated Logout 1.0 §2
|
|
506
|
+
* requires the two to agree, so that request is a 400 `invalid_request`
|
|
507
|
+
* and ends no session.
|
|
511
508
|
*
|
|
512
509
|
* **Authorizing `post_logout_redirect_uri` is the whole security surface.**
|
|
513
510
|
* It is matched by **exact string** against the resolved client's
|
|
@@ -519,11 +516,14 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
519
516
|
* registered, the browser is **not** sent there — it goes to `fallback` if
|
|
520
517
|
* the app supplied one, or gets a bare 204.
|
|
521
518
|
*
|
|
522
|
-
* `id_token_hint` is verified against the signing
|
|
523
|
-
*
|
|
524
|
-
*
|
|
525
|
-
*
|
|
526
|
-
*
|
|
519
|
+
* `id_token_hint` is verified against the current signing key only — a hint
|
|
520
|
+
* signed by a key since rotated out of signing yields no subject — and its
|
|
521
|
+
* expiry is ignored: by the time anyone logs out the id_token naming their
|
|
522
|
+
* session has usually expired, and it is a hint, not authority. `client_id`,
|
|
523
|
+
* when sent, names the client ahead of the hint's audience, and when both
|
|
524
|
+
* are sent `client_id` must be one of the hint's audiences — a mismatch is
|
|
525
|
+
* a 400 `invalid_request` (OIDC RP-Initiated Logout 1.0 §2). `state` rides
|
|
526
|
+
* along only to a URI that passed authorization.
|
|
527
527
|
*
|
|
528
528
|
* Wire the app's session teardown through the `endSession` **constructor
|
|
529
529
|
* option**; this method answers 404 without it, and the discovery document
|
|
@@ -568,12 +568,7 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
568
568
|
return this.handleError(error);
|
|
569
569
|
}
|
|
570
570
|
}
|
|
571
|
-
|
|
572
|
-
* The logout parameters, from the query string on GET and the form body on
|
|
573
|
-
* POST — RP-Initiated Logout §2 permits both. A verified `id_token_hint`
|
|
574
|
-
* contributes the subject and the audience; an unverifiable one contributes
|
|
575
|
-
* nothing rather than refusing the logout.
|
|
576
|
-
*/
|
|
571
|
+
// RP-Initiated Logout §2: parameters come from the GET query or POST body.
|
|
577
572
|
async #endSessionParameters(request) {
|
|
578
573
|
const url = new URL(request.url);
|
|
579
574
|
const params = request.method === "POST"
|
|
@@ -581,15 +576,19 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
581
576
|
: url.searchParams;
|
|
582
577
|
const idTokenHint = params.get("id_token_hint");
|
|
583
578
|
const hint = idTokenHint ? await this.#readIdTokenHint(idTokenHint) : null;
|
|
579
|
+
const clientId = params.get("client_id");
|
|
580
|
+
if (clientId && hint && hint.audiences.length > 0 &&
|
|
581
|
+
!hint.audiences.includes(clientId)) {
|
|
582
|
+
throw new InvalidRequestError("client_id does not match the audience of id_token_hint");
|
|
583
|
+
}
|
|
584
584
|
return {
|
|
585
|
-
clientId:
|
|
585
|
+
clientId: clientId ?? hint?.audiences[0] ?? null,
|
|
586
586
|
subject: hint?.subject ?? null,
|
|
587
587
|
postLogoutRedirectUri: params.get("post_logout_redirect_uri") ?? null,
|
|
588
588
|
state: params.get("state") ?? null,
|
|
589
589
|
logoutHint: params.get("logout_hint") ?? null,
|
|
590
590
|
};
|
|
591
591
|
}
|
|
592
|
-
/** The subject and audience of a signature-valid `id_token_hint`, if any. */
|
|
593
592
|
async #readIdTokenHint(idTokenHint) {
|
|
594
593
|
const key = await this.signingKeys?.getSigningKey();
|
|
595
594
|
if (!key)
|
|
@@ -600,17 +599,16 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
600
599
|
if (!payload)
|
|
601
600
|
return null;
|
|
602
601
|
const aud = payload.aud;
|
|
603
|
-
const
|
|
604
|
-
? aud
|
|
605
|
-
: Array.isArray(aud)
|
|
606
|
-
? aud
|
|
607
|
-
:
|
|
602
|
+
const audiences = typeof aud === "string"
|
|
603
|
+
? [aud]
|
|
604
|
+
: Array.isArray(aud)
|
|
605
|
+
? aud.filter((value) => typeof value === "string")
|
|
606
|
+
: [];
|
|
608
607
|
return {
|
|
609
608
|
subject: typeof payload.sub === "string" ? payload.sub : null,
|
|
610
|
-
|
|
609
|
+
audiences,
|
|
611
610
|
};
|
|
612
611
|
}
|
|
613
|
-
/** The client the logout names, or null when it named none we can find. */
|
|
614
612
|
async #resolveEndSessionClient(request, params) {
|
|
615
613
|
if (!params.clientId)
|
|
616
614
|
return null;
|
|
@@ -622,11 +620,6 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
622
620
|
return null;
|
|
623
621
|
}
|
|
624
622
|
}
|
|
625
|
-
/**
|
|
626
|
-
* The `post_logout_redirect_uri` to honor, with `state` appended — or
|
|
627
|
-
* undefined when the request named none, named no resolvable client, or
|
|
628
|
-
* named a URI that client did not register.
|
|
629
|
-
*/
|
|
630
623
|
#postLogoutRedirect(client, params) {
|
|
631
624
|
const requested = params.postLogoutRedirectUri;
|
|
632
625
|
if (!requested || !client)
|
|
@@ -643,8 +636,8 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
643
636
|
/**
|
|
644
637
|
* Parses authorization request parameters from the query string.
|
|
645
638
|
*
|
|
646
|
-
*
|
|
647
|
-
* and
|
|
639
|
+
* Reads only `url.searchParams`: RFC 6749 Section 3.1 requires GET support
|
|
640
|
+
* and leaves POST optional, and this server serves the endpoint on GET.
|
|
648
641
|
*/
|
|
649
642
|
parseAuthorizeParameters(request) {
|
|
650
643
|
const url = new URL(request.url);
|
|
@@ -667,10 +660,12 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
667
660
|
* Handles an authorization request (GET /authorize).
|
|
668
661
|
* Used for the authorization code flow.
|
|
669
662
|
*
|
|
670
|
-
* Once
|
|
671
|
-
*
|
|
672
|
-
*
|
|
673
|
-
*
|
|
663
|
+
* Once a redirect target is verified — the requested `redirect_uri` matched
|
|
664
|
+
* a registration, or the request omitted it and the client has a literal
|
|
665
|
+
* registration, whose first entry is used — failures are reported to it as
|
|
666
|
+
* an RFC 6749 Section 4.1.2.1 error redirect; before that they are error
|
|
667
|
+
* responses from this endpoint, since there is no verified place to send the
|
|
668
|
+
* user back to.
|
|
674
669
|
*
|
|
675
670
|
* Requires a grant extending {@linkcode AuthorizationCodeGrant} registered
|
|
676
671
|
* under `authorization_code`; without one the endpoint answers 500
|
|
@@ -690,11 +685,6 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
690
685
|
return this.#authorizeErrorResponse(error, target.url);
|
|
691
686
|
}
|
|
692
687
|
}
|
|
693
|
-
/**
|
|
694
|
-
* The authorize flow proper, recording the redirect target on `target` as
|
|
695
|
-
* soon as one is verified so {@link handleAuthorizeRequest} can report later
|
|
696
|
-
* failures to the client.
|
|
697
|
-
*/
|
|
698
688
|
async #authorize(request, target, authenticateUser, handleConsent) {
|
|
699
689
|
const params = this.parseAuthorizeParameters(request);
|
|
700
690
|
const grant = this.#authorizationCodeGrant();
|
|
@@ -744,10 +734,6 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
744
734
|
redirectUrl.searchParams.set("code", authorizationCode.code);
|
|
745
735
|
return Response.redirect(redirectUrl.toString(), 302);
|
|
746
736
|
}
|
|
747
|
-
/**
|
|
748
|
-
* Reports an authorize failure: an RFC 6749 Section 4.1.2.1 error redirect
|
|
749
|
-
* once a verified `redirectUrl` exists, otherwise a plain error response.
|
|
750
|
-
*/
|
|
751
737
|
#authorizeErrorResponse(error, redirectUrl) {
|
|
752
738
|
if (!redirectUrl || !(error instanceof Error) || !isOAuth2Error(error)) {
|
|
753
739
|
return this.handleError(error);
|
|
@@ -790,10 +776,6 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
790
776
|
}
|
|
791
777
|
return grant;
|
|
792
778
|
}
|
|
793
|
-
/**
|
|
794
|
-
* The client the authorize request names, once it is registered for the
|
|
795
|
-
* authorization code grant.
|
|
796
|
-
*/
|
|
797
779
|
async #resolveAuthorizeClient(grant, clientId, services) {
|
|
798
780
|
if (!clientId) {
|
|
799
781
|
throw new InvalidRequestError("client_id parameter required");
|
|
@@ -802,11 +784,6 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
802
784
|
this.#assertGrantAuthorized(client, AUTHORIZATION_CODE_GRANT_TYPE);
|
|
803
785
|
return client;
|
|
804
786
|
}
|
|
805
|
-
/**
|
|
806
|
-
* The URL the browser is sent back to: the requested `redirect_uri` once it
|
|
807
|
-
* matches a registration, or the client's first literal registration when the
|
|
808
|
-
* request omitted one.
|
|
809
|
-
*/
|
|
810
787
|
#resolveRedirectTarget(client, redirectUri) {
|
|
811
788
|
if (!client.redirectUris?.length) {
|
|
812
789
|
throw new UnauthorizedClientError("no authorized redirect_uri");
|
|
@@ -821,7 +798,6 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
821
798
|
}
|
|
822
799
|
return new URL(redirectUri ?? defaultRedirectUri);
|
|
823
800
|
}
|
|
824
|
-
/** Rejects any `response_type` other than the `code` this endpoint issues. */
|
|
825
801
|
#validateResponseType(responseType) {
|
|
826
802
|
if (!responseType) {
|
|
827
803
|
throw new InvalidRequestError("response_type required");
|
|
@@ -831,10 +807,6 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
831
807
|
throw new UnsupportedResponseTypeError("response_type not supported");
|
|
832
808
|
}
|
|
833
809
|
}
|
|
834
|
-
/**
|
|
835
|
-
* Rejects PKCE parameters the grant cannot honor (RFC 7636 Section 4.3), and
|
|
836
|
-
* a `code_challenge` malformed for a method whose shape Section 4.2 fixes.
|
|
837
|
-
*/
|
|
838
810
|
#validateChallenge(grant, params) {
|
|
839
811
|
const { challenge, challengeMethod } = params;
|
|
840
812
|
if (challengeMethod && !challenge) {
|
|
@@ -847,11 +819,6 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
847
819
|
throw new InvalidRequestError("code_challenge must be 43-128 characters using [A-Z] / [a-z] / [0-9] / - / . / _ / ~");
|
|
848
820
|
}
|
|
849
821
|
}
|
|
850
|
-
/**
|
|
851
|
-
* The scope the authorization code will carry: the requested scope narrowed
|
|
852
|
-
* by the token service, then by consent when the user has not already
|
|
853
|
-
* pre-authorized it. A `Response` short-circuits the flow (a consent page).
|
|
854
|
-
*/
|
|
855
822
|
async #resolveScopeAndConsent(options) {
|
|
856
823
|
const { grant, client, user, requestedScope, authorizedScope, services, handleConsent, } = options;
|
|
857
824
|
const acceptedScope = await grant.acceptedScope(client, user, requestedScope, services.tokenService);
|
|
@@ -972,8 +939,11 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
972
939
|
* that field describes how an access token is presented, and its `exp` is
|
|
973
940
|
* the refresh token's own expiry.
|
|
974
941
|
*
|
|
975
|
-
* A token with
|
|
976
|
-
*
|
|
942
|
+
* A token with a user carries `sub` from `subjectOf` — the same value the
|
|
943
|
+
* id_token and UserInfo carry — and `username` when the user has one. A
|
|
944
|
+
* token with no user — the client acting as its own resource owner, as the
|
|
945
|
+
* client credentials grant issues — carries neither, even when
|
|
946
|
+
* {@linkcode AuthorizationServerOptions.introspectionClaims} returns them;
|
|
977
947
|
* `client_id` identifies the machine. `sub` is optional in RFC 7662, and
|
|
978
948
|
* leaving it absent keeps `sub` an unambiguous "there is a resource owner"
|
|
979
949
|
* signal rather than overloading `client_id` into the subject namespace
|
|
@@ -1022,16 +992,18 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
1022
992
|
if (context.issuer) {
|
|
1023
993
|
response.iss = context.issuer;
|
|
1024
994
|
}
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
995
|
+
if (token.user) {
|
|
996
|
+
response.sub = this.#subjectOf(token.user);
|
|
997
|
+
const username = token.user.username;
|
|
998
|
+
if (typeof username === "string" && username) {
|
|
999
|
+
response.username = username;
|
|
1000
|
+
}
|
|
1031
1001
|
}
|
|
1032
1002
|
if (this.#introspectionClaims) {
|
|
1003
|
+
const extensions = await this.#introspectionClaims(token);
|
|
1004
|
+
const { sub: _sub, username: _username, ...machineExtensions } = extensions;
|
|
1033
1005
|
response = {
|
|
1034
|
-
...
|
|
1006
|
+
...(token.user ? extensions : machineExtensions),
|
|
1035
1007
|
...response,
|
|
1036
1008
|
};
|
|
1037
1009
|
}
|
|
@@ -1118,7 +1090,6 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
1118
1090
|
const { challengeMethods } = grant;
|
|
1119
1091
|
return Object.keys(challengeMethods).filter((method) => typeof challengeMethods[method] === "function");
|
|
1120
1092
|
}
|
|
1121
|
-
/** Publishes the resolved URLs of the OIDC (or plain OAuth2) endpoints. */
|
|
1122
1093
|
#advertiseEndpoints(metadata, endpoints, oidc) {
|
|
1123
1094
|
for (const [key, endpoint] of ADVERTISED_ENTRIES) {
|
|
1124
1095
|
if (endpoint.oidc !== oidc)
|
|
@@ -1222,13 +1193,18 @@ export class AuthorizationServer extends ResourceServer {
|
|
|
1222
1193
|
* - `POST {revocationEndpoint}` → `handleRevocationRequest`
|
|
1223
1194
|
* - `POST {introspectionEndpoint}` → `handleIntrospectionRequest`
|
|
1224
1195
|
* - `POST {deviceAuthorizationEndpoint}` → `handleDeviceAuthorizationRequest`
|
|
1225
|
-
* - `GET
|
|
1196
|
+
* - `GET {jwksEndpoint}` → `handleJwksRequest`
|
|
1197
|
+
* - `GET`/`POST {userinfoEndpoint}` → `handleUserInfoRequest`
|
|
1198
|
+
* - `GET /.well-known/oauth-authorization-server` → `handleMetadataRequest`
|
|
1199
|
+
* - `GET /.well-known/openid-configuration` → `handleOidcMetadataRequest`
|
|
1200
|
+
*
|
|
1201
|
+
* The two discovery paths are matched literally, not against the issuer.
|
|
1226
1202
|
*
|
|
1227
|
-
* The authorization
|
|
1228
|
-
*
|
|
1229
|
-
*
|
|
1230
|
-
*
|
|
1231
|
-
*
|
|
1203
|
+
* The authorization and end-session endpoints are deliberately **not**
|
|
1204
|
+
* routed: in real flows both are browser navigations, never calls through the
|
|
1205
|
+
* client's `fetch`, and `/authorize` needs an `authenticateUser` (and optional
|
|
1206
|
+
* `handleConsent`) callback this transport has no access to. A request to
|
|
1207
|
+
* either throws an error naming the handler to drive directly. Any other path also
|
|
1232
1208
|
* throws — the helper intentionally does not proxy arbitrary paths; if a call
|
|
1233
1209
|
* lands here unexpectedly, that's a wiring bug worth surfacing rather than
|
|
1234
1210
|
* silently forwarding.
|
|
@@ -24,7 +24,7 @@ export interface AuthorizationCodeGrantServices<Client extends ClientInterface,
|
|
|
24
24
|
* @see https://datatracker.ietf.org/doc/html/rfc7636
|
|
25
25
|
*/
|
|
26
26
|
export interface AuthorizationCodeGrantOptions<Client extends ClientInterface, User, S extends AbstractScope> extends GrantOptions<Client, User, S, AuthorizationCodeGrantServices<Client, User, S>> {
|
|
27
|
-
/**
|
|
27
|
+
/** Whether to issue a refresh token with the access token. Defaults to `true`. */
|
|
28
28
|
allowRefreshToken?: boolean;
|
|
29
29
|
/** Custom PKCE challenge methods. Defaults to S256 only. */
|
|
30
30
|
challengeMethods?: ChallengeMethods;
|
|
@@ -166,8 +166,12 @@ export declare class AuthorizationCodeGrant<Client extends ClientInterface, User
|
|
|
166
166
|
* Exchanges an authorization code for an access token. If PKCE was used
|
|
167
167
|
* during authorization, the code_verifier must be provided.
|
|
168
168
|
*
|
|
169
|
-
* The code is claimed by revoking it
|
|
170
|
-
*
|
|
169
|
+
* The code is claimed by revoking it before the client-match, verifier, and
|
|
170
|
+
* `redirect_uri` checks, so a failed exchange still consumes the code. Only
|
|
171
|
+
* the caller whose revoke wins receives a token, given an atomic
|
|
172
|
+
* `authorizationCodeService.revoke`. Presenting a code that already minted
|
|
173
|
+
* tokens asks `tokenService.revokeCode` to revoke those tokens and fails with
|
|
174
|
+
* `invalid_grant` (RFC 6819 §4.4.1.1).
|
|
171
175
|
*
|
|
172
176
|
* @throws {InvalidRequestError} When `code` is missing, a required `code_verifier` is absent, or the verifier is malformed.
|
|
173
177
|
* @throws {InvalidGrantError} When the code is unknown, expired, already claimed, or its verifier or `redirect_uri` does not match.
|
|
@@ -164,8 +164,12 @@ export class AuthorizationCodeGrant extends AbstractGrant {
|
|
|
164
164
|
* Exchanges an authorization code for an access token. If PKCE was used
|
|
165
165
|
* during authorization, the code_verifier must be provided.
|
|
166
166
|
*
|
|
167
|
-
* The code is claimed by revoking it
|
|
168
|
-
*
|
|
167
|
+
* The code is claimed by revoking it before the client-match, verifier, and
|
|
168
|
+
* `redirect_uri` checks, so a failed exchange still consumes the code. Only
|
|
169
|
+
* the caller whose revoke wins receives a token, given an atomic
|
|
170
|
+
* `authorizationCodeService.revoke`. Presenting a code that already minted
|
|
171
|
+
* tokens asks `tokenService.revokeCode` to revoke those tokens and fails with
|
|
172
|
+
* `invalid_grant` (RFC 6819 §4.4.1.1).
|
|
169
173
|
*
|
|
170
174
|
* @throws {InvalidRequestError} When `code` is missing, a required `code_verifier` is absent, or the verifier is malformed.
|
|
171
175
|
* @throws {InvalidGrantError} When the code is unknown, expired, already claimed, or its verifier or `redirect_uri` does not match.
|
|
@@ -27,7 +27,7 @@ export interface DeviceAuthorizationGrantServices<Client extends ClientInterface
|
|
|
27
27
|
* @see https://datatracker.ietf.org/doc/html/rfc8628
|
|
28
28
|
*/
|
|
29
29
|
export interface DeviceAuthorizationGrantOptions<Client extends ClientInterface, User, S extends AbstractScope> extends GrantOptions<Client, User, S, DeviceAuthorizationGrantServices<Client, User, S>> {
|
|
30
|
-
/**
|
|
30
|
+
/** Whether to issue a refresh token with the access token. Defaults to `true`. */
|
|
31
31
|
allowRefreshToken?: boolean;
|
|
32
32
|
}
|
|
33
33
|
/**
|
|
@@ -72,7 +72,11 @@ export interface GrantOptions<Client extends ClientInterface, User, Scope extend
|
|
|
72
72
|
resolve: (request: Request) => Services | Promise<Services>;
|
|
73
73
|
/** Scope constructor used to parse scope strings. Defaults to {@linkcode BasicScope}. */
|
|
74
74
|
Scope?: ScopeConstructor<Scope>;
|
|
75
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* Whether to issue a refresh token with tokens that have a user, when the
|
|
77
|
+
* token service generates one. Defaults to `false`; tokens without a user
|
|
78
|
+
* never get one.
|
|
79
|
+
*/
|
|
76
80
|
allowRefreshToken?: boolean;
|
|
77
81
|
}
|
|
78
82
|
/**
|
|
@@ -82,8 +86,7 @@ export interface GrantOptions<Client extends ClientInterface, User, Scope extend
|
|
|
82
86
|
* interface the grant resolves. A subclass implements {@link token}, reaches
|
|
83
87
|
* its services through {@link resolveServices}, and overrides
|
|
84
88
|
* {@link getAuthenticatedClient} only when it authenticates clients by
|
|
85
|
-
* something other than their registered credentials
|
|
86
|
-
* grant does for PKCE).
|
|
89
|
+
* something other than their registered credentials.
|
|
87
90
|
*/
|
|
88
91
|
export declare abstract class AbstractGrant<Client extends ClientInterface, User, Scope extends AbstractScope = BasicScope, Services extends GrantServices<Client, User, Scope> = GrantServices<Client, User, Scope>> implements DispatchableGrant<Client, User, Scope> {
|
|
89
92
|
#private;
|
|
@@ -91,7 +94,7 @@ export declare abstract class AbstractGrant<Client extends ClientInterface, User
|
|
|
91
94
|
abstract readonly grantType: string;
|
|
92
95
|
/** Constructor used to parse scope strings into {@linkcode Scope} objects. */
|
|
93
96
|
Scope: ScopeConstructor<Scope>;
|
|
94
|
-
/**
|
|
97
|
+
/** Whether tokens with a user are issued a refresh token. */
|
|
95
98
|
allowRefreshToken: boolean;
|
|
96
99
|
/** Creates a grant from the shared {@linkcode GrantOptions}. */
|
|
97
100
|
constructor(options: GrantOptions<Client, User, Scope, Services>);
|
|
@@ -142,16 +145,20 @@ export declare abstract class AbstractGrant<Client extends ClientInterface, User
|
|
|
142
145
|
* owner whose authorization a rotation could carry forward.
|
|
143
146
|
* `authenticationContext` is verified event evidence, snapshotted before
|
|
144
147
|
* generation and carried to storage. Omit it when unknown; it is discarded
|
|
145
|
-
* for tokens with no user.
|
|
148
|
+
* for tokens with no user. `familyExpiresAt` is the ceiling of an existing
|
|
149
|
+
* rotation family the token joins (the refresh grant passes it); the
|
|
150
|
+
* expiries are clamped to it before the access token string is generated,
|
|
151
|
+
* so a signed token's `exp` matches the stored expiry.
|
|
146
152
|
*/
|
|
147
|
-
generateToken(client: Client, user: User | undefined, scope: Scope | null | undefined, tokenService: TokenServiceInterface<Client, User, Scope>, authenticationContext?: AuthenticationContext): Promise<Token<Client, User, Scope>>;
|
|
153
|
+
generateToken(client: Client, user: User | undefined, scope: Scope | null | undefined, tokenService: TokenServiceInterface<Client, User, Scope>, authenticationContext?: AuthenticationContext, familyExpiresAt?: Date): Promise<Token<Client, User, Scope>>;
|
|
148
154
|
/**
|
|
149
155
|
* Authenticates the client presenting this grant's token request against the
|
|
150
156
|
* grant's own `clientService`, from the credentials the request presents.
|
|
151
157
|
*
|
|
152
158
|
* Override to apply grant-specific authentication rules — the
|
|
153
|
-
* authorization-code grant
|
|
154
|
-
* client
|
|
159
|
+
* authorization-code grant, when configured with
|
|
160
|
+
* `requireClientAuthentication: false`, looks up a client that presents a
|
|
161
|
+
* PKCE `code_verifier` by id instead of authenticating it.
|
|
155
162
|
*
|
|
156
163
|
* @throws {InvalidClientError} If authentication fails.
|
|
157
164
|
*/
|
|
@@ -27,13 +27,12 @@ import { authenticateClientCredentials, extractClientCredentials, } from "../cli
|
|
|
27
27
|
* interface the grant resolves. A subclass implements {@link token}, reaches
|
|
28
28
|
* its services through {@link resolveServices}, and overrides
|
|
29
29
|
* {@link getAuthenticatedClient} only when it authenticates clients by
|
|
30
|
-
* something other than their registered credentials
|
|
31
|
-
* grant does for PKCE).
|
|
30
|
+
* something other than their registered credentials.
|
|
32
31
|
*/
|
|
33
32
|
export class AbstractGrant {
|
|
34
33
|
/** Constructor used to parse scope strings into {@linkcode Scope} objects. */
|
|
35
34
|
Scope;
|
|
36
|
-
/**
|
|
35
|
+
/** Whether tokens with a user are issued a refresh token. */
|
|
37
36
|
allowRefreshToken;
|
|
38
37
|
#resolve;
|
|
39
38
|
/** Creates a grant from the shared {@linkcode GrantOptions}. */
|
|
@@ -114,16 +113,16 @@ export class AbstractGrant {
|
|
|
114
113
|
* owner whose authorization a rotation could carry forward.
|
|
115
114
|
* `authenticationContext` is verified event evidence, snapshotted before
|
|
116
115
|
* generation and carried to storage. Omit it when unknown; it is discarded
|
|
117
|
-
* for tokens with no user.
|
|
116
|
+
* for tokens with no user. `familyExpiresAt` is the ceiling of an existing
|
|
117
|
+
* rotation family the token joins (the refresh grant passes it); the
|
|
118
|
+
* expiries are clamped to it before the access token string is generated,
|
|
119
|
+
* so a signed token's `exp` matches the stored expiry.
|
|
118
120
|
*/
|
|
119
|
-
async generateToken(client, user, scope, tokenService, authenticationContext) {
|
|
121
|
+
async generateToken(client, user, scope, tokenService, authenticationContext, familyExpiresAt) {
|
|
120
122
|
const event = user === undefined
|
|
121
123
|
? undefined
|
|
122
124
|
: snapshotAuthenticationContext(authenticationContext);
|
|
123
|
-
const token = {
|
|
124
|
-
accessToken: await tokenService.generateAccessToken(client, user, scope, event),
|
|
125
|
-
client,
|
|
126
|
-
};
|
|
125
|
+
const token = { accessToken: "", client };
|
|
127
126
|
if (event)
|
|
128
127
|
token.authenticationContext = event;
|
|
129
128
|
if (user !== undefined)
|
|
@@ -133,11 +132,13 @@ export class AbstractGrant {
|
|
|
133
132
|
const accessTokenExpiresAt = await tokenService.accessTokenExpiresAt(client, user, scope);
|
|
134
133
|
if (accessTokenExpiresAt)
|
|
135
134
|
token.accessTokenExpiresAt = accessTokenExpiresAt;
|
|
135
|
+
let result = token;
|
|
136
|
+
let ceiling = familyExpiresAt;
|
|
136
137
|
if (this.allowRefreshToken && user !== undefined) {
|
|
137
138
|
const refreshToken = await tokenService.generateRefreshToken(client, user, scope);
|
|
138
139
|
if (refreshToken) {
|
|
139
140
|
const familyCreatedAt = new Date();
|
|
140
|
-
const
|
|
141
|
+
const withRefresh = {
|
|
141
142
|
...token,
|
|
142
143
|
refreshToken,
|
|
143
144
|
familyId: crypto.randomUUID(),
|
|
@@ -145,24 +146,31 @@ export class AbstractGrant {
|
|
|
145
146
|
};
|
|
146
147
|
const refreshTokenExpiresAt = await tokenService.refreshTokenExpiresAt(client, user, scope);
|
|
147
148
|
if (refreshTokenExpiresAt) {
|
|
148
|
-
|
|
149
|
+
withRefresh.refreshTokenExpiresAt = refreshTokenExpiresAt;
|
|
149
150
|
}
|
|
150
|
-
const
|
|
151
|
+
const newFamilyExpiresAt = await tokenService
|
|
151
152
|
.refreshTokenFamilyExpiresAt?.(client, user, familyCreatedAt, scope);
|
|
152
|
-
if (
|
|
153
|
-
|
|
154
|
-
|
|
153
|
+
if (newFamilyExpiresAt) {
|
|
154
|
+
ceiling = ceiling && ceiling < newFamilyExpiresAt
|
|
155
|
+
? ceiling
|
|
156
|
+
: newFamilyExpiresAt;
|
|
157
|
+
}
|
|
158
|
+
result = withRefresh;
|
|
155
159
|
}
|
|
156
160
|
}
|
|
157
|
-
|
|
161
|
+
if (ceiling)
|
|
162
|
+
this.capAtFamilyExpiry(result, ceiling);
|
|
163
|
+
result.accessToken = await tokenService.generateAccessToken(client, user, scope, event, result.accessTokenExpiresAt);
|
|
164
|
+
return result;
|
|
158
165
|
}
|
|
159
166
|
/**
|
|
160
167
|
* Authenticates the client presenting this grant's token request against the
|
|
161
168
|
* grant's own `clientService`, from the credentials the request presents.
|
|
162
169
|
*
|
|
163
170
|
* Override to apply grant-specific authentication rules — the
|
|
164
|
-
* authorization-code grant
|
|
165
|
-
* client
|
|
171
|
+
* authorization-code grant, when configured with
|
|
172
|
+
* `requireClientAuthentication: false`, looks up a client that presents a
|
|
173
|
+
* PKCE `code_verifier` by id instead of authenticating it.
|
|
166
174
|
*
|
|
167
175
|
* @throws {InvalidClientError} If authentication fails.
|
|
168
176
|
*/
|
|
@@ -29,7 +29,7 @@ export interface PasswordGrantServices<Client extends ClientInterface, User, S e
|
|
|
29
29
|
}
|
|
30
30
|
/** Options for configuring the password grant. */
|
|
31
31
|
export interface PasswordGrantOptions<Client extends ClientInterface, User, S extends AbstractScope> extends GrantOptions<Client, User, S, PasswordGrantServices<Client, User, S>> {
|
|
32
|
-
/**
|
|
32
|
+
/** Whether to issue a refresh token with the access token. Defaults to `true`. */
|
|
33
33
|
allowRefreshToken?: boolean;
|
|
34
34
|
}
|
|
35
35
|
/**
|
|
@@ -30,7 +30,9 @@ export interface TokenReuseEvent<Client extends ClientInterface, User> {
|
|
|
30
30
|
export interface RefreshTokenGrantOptions<Client extends ClientInterface, User, S extends AbstractScope> extends Omit<GrantOptions<Client, User, S, RefreshTokenGrantServices<Client, User, S>>, "allowRefreshToken"> {
|
|
31
31
|
/**
|
|
32
32
|
* Called when reuse detection catches a rotated-out refresh token being
|
|
33
|
-
* replayed (after the family was revoked) —
|
|
33
|
+
* replayed by the client it was issued to (after the family was revoked) —
|
|
34
|
+
* record it as a security event. Reuse detection runs only when the token
|
|
35
|
+
* service implements both `getRevokedRefreshToken` and `revokeFamily`.
|
|
34
36
|
*
|
|
35
37
|
* Trigger point `token-reuse` (see `docs/trigger-points.md`).
|
|
36
38
|
* Fire-and-forget: it is awaited, but a throw is caught and logged, never
|
|
@@ -71,8 +73,9 @@ export declare class RefreshTokenGrant<Client extends ClientInterface, User, S e
|
|
|
71
73
|
* authorization.
|
|
72
74
|
*
|
|
73
75
|
* @throws {InvalidRequestError} If `refresh_token` is missing.
|
|
74
|
-
* @throws {InvalidGrantError} If the refresh token is unknown or expired,
|
|
75
|
-
*
|
|
76
|
+
* @throws {InvalidGrantError} If the refresh token is unknown or expired, its
|
|
77
|
+
* family has outlived the absolute cap, or a concurrent exchange already
|
|
78
|
+
* claimed it.
|
|
76
79
|
* @throws {InvalidClientError} If the token was issued to another client.
|
|
77
80
|
* @throws {InvalidScopeError} If the requested scope exceeds the original grant.
|
|
78
81
|
*/
|