@udibo/oauth2 0.11.0 → 0.12.0
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.
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates clock-skew configuration shared by token readers and resource
|
|
3
|
+
* servers.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
/** Throws `RangeError` unless the leeway is non-negative, finite seconds whose millisecond conversion is finite. */
|
|
8
|
+
export declare function assertClockSkewSeconds(clockSkewSeconds: number): void;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Validates clock-skew configuration shared by token readers and resource
|
|
3
|
+
* servers.
|
|
4
|
+
*
|
|
5
|
+
* @module
|
|
6
|
+
*/
|
|
7
|
+
/** Throws `RangeError` unless the leeway is non-negative, finite seconds whose millisecond conversion is finite. */
|
|
8
|
+
export function assertClockSkewSeconds(clockSkewSeconds) {
|
|
9
|
+
if (!Number.isFinite(clockSkewSeconds) || clockSkewSeconds < 0 ||
|
|
10
|
+
!Number.isFinite(clockSkewSeconds * 1000)) {
|
|
11
|
+
throw new RangeError("clockSkewSeconds must be non-negative finite seconds with a finite millisecond conversion");
|
|
12
|
+
}
|
|
13
|
+
}
|
|
@@ -110,7 +110,9 @@ export interface JwksTokenReaderOptions<Client extends ClientInterface, User, S
|
|
|
110
110
|
clientIdClaim?: string;
|
|
111
111
|
/**
|
|
112
112
|
* Leeway in seconds applied to `exp` and `nbf` to absorb clock drift
|
|
113
|
-
* between the issuer and this server. Defaults to 30.
|
|
113
|
+
* between the issuer and this server. Defaults to 30. Must be finite and
|
|
114
|
+
* non-negative, with a finite millisecond conversion; fractional seconds
|
|
115
|
+
* are supported.
|
|
114
116
|
*
|
|
115
117
|
* This governs this reader's own claim checks. The token it returns carries
|
|
116
118
|
* the issuer's `exp` verbatim, and {@linkcode ResourceServer} re-checks that
|
|
@@ -237,6 +239,8 @@ export declare class JwksTokenReader<Client extends ClientInterface, User, S ext
|
|
|
237
239
|
* @throws {TypeError} If `issuer` is not an absolute URL, `audience` is
|
|
238
240
|
* empty, or `algorithms` names an algorithm this package cannot verify
|
|
239
241
|
* (a silently ignored entry would read as a policy that isn't enforced).
|
|
242
|
+
* @throws {RangeError} If `clockSkewSeconds` is negative, non-finite, or
|
|
243
|
+
* overflows when converted to milliseconds.
|
|
240
244
|
*/
|
|
241
245
|
constructor(options: JwksTokenReaderOptions<Client, User, S>);
|
|
242
246
|
/**
|
|
@@ -17,6 +17,7 @@ import { BasicScope } from "../models/scope.js";
|
|
|
17
17
|
import { toArrayBuffer } from "../utils/_buffer.js";
|
|
18
18
|
import { base64urlDecode } from "../utils/crypto.js";
|
|
19
19
|
import { defaultFetch, packageDeadlineSignal, } from "../utils/_default-fetch.js";
|
|
20
|
+
import { assertClockSkewSeconds } from "./_clock-skew.js";
|
|
20
21
|
const ALGORITHMS = {
|
|
21
22
|
ES256: {
|
|
22
23
|
kty: "EC",
|
|
@@ -229,6 +230,8 @@ export class JwksTokenReader {
|
|
|
229
230
|
* @throws {TypeError} If `issuer` is not an absolute URL, `audience` is
|
|
230
231
|
* empty, or `algorithms` names an algorithm this package cannot verify
|
|
231
232
|
* (a silently ignored entry would read as a policy that isn't enforced).
|
|
233
|
+
* @throws {RangeError} If `clockSkewSeconds` is negative, non-finite, or
|
|
234
|
+
* overflows when converted to milliseconds.
|
|
232
235
|
*/
|
|
233
236
|
constructor(options) {
|
|
234
237
|
this.#issuer = options.issuer;
|
|
@@ -248,6 +251,7 @@ export class JwksTokenReader {
|
|
|
248
251
|
this.#types = (options.types ?? ["at+jwt"]).map(normalizeType);
|
|
249
252
|
this.#clientIdClaim = options.clientIdClaim ?? "client_id";
|
|
250
253
|
this.#clockSkewSeconds = options.clockSkewSeconds ?? 30;
|
|
254
|
+
assertClockSkewSeconds(this.#clockSkewSeconds);
|
|
251
255
|
this.#cacheMaxAgeMs = options.cacheMaxAgeMs ?? 600_000;
|
|
252
256
|
this.#minFetchIntervalMs = options.minFetchIntervalMs ?? 30_000;
|
|
253
257
|
this.#fetchTimeoutMs = options.fetchTimeoutMs ?? 5_000;
|
|
@@ -43,7 +43,9 @@ export interface ResourceServerOptions<Client extends ClientInterface, User, S e
|
|
|
43
43
|
/**
|
|
44
44
|
* Leeway in seconds applied to the access token's expiry, to absorb clock
|
|
45
45
|
* drift between whoever issued the token and this server. Defaults to `0`:
|
|
46
|
-
* a token whose expiry has passed is refused immediately.
|
|
46
|
+
* a token whose expiry has passed is refused immediately. Must be finite and
|
|
47
|
+
* non-negative, with a finite millisecond conversion; fractional seconds
|
|
48
|
+
* are supported.
|
|
47
49
|
*
|
|
48
50
|
* This is the one expiry check every token reader's result passes through,
|
|
49
51
|
* which is why the leeway belongs here rather than in a reader:
|
|
@@ -138,15 +140,31 @@ export declare class ResourceServer<Client extends ClientInterface, User, S exte
|
|
|
138
140
|
Scope: ScopeConstructor<S>;
|
|
139
141
|
/** Protection space named in `WWW-Authenticate` challenges. */
|
|
140
142
|
realm: string;
|
|
141
|
-
/** Leeway in seconds applied to the access token's expiry. Defaults to `0`. */
|
|
142
|
-
clockSkewSeconds: number;
|
|
143
143
|
/** Body format used for error responses: OAuth2 or RFC 9457 problem details. */
|
|
144
144
|
errorFormat: "oauth2" | "problem-details";
|
|
145
145
|
/** When true, handler boundaries rethrow {@linkcode OAuth2Error} instead of returning a response. */
|
|
146
146
|
throwOnError: boolean;
|
|
147
147
|
/** Resolves the per-request {@link ResourceServerContext}. */
|
|
148
148
|
protected resolveContext: (request: Request) => ResourceServerContext<Client, User, S> | Promise<ResourceServerContext<Client, User, S>>;
|
|
149
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* Leeway in seconds applied to the access token's expiry. Defaults to `0`.
|
|
151
|
+
* May be reassigned to any non-negative finite number with a finite
|
|
152
|
+
* millisecond conversion, including fractional seconds.
|
|
153
|
+
*/
|
|
154
|
+
get clockSkewSeconds(): number;
|
|
155
|
+
/**
|
|
156
|
+
* Sets the expiry leeway, preserving the previous value if validation fails.
|
|
157
|
+
*
|
|
158
|
+
* @throws {RangeError} If the value is negative, non-finite, or overflows
|
|
159
|
+
* when converted to milliseconds.
|
|
160
|
+
*/
|
|
161
|
+
set clockSkewSeconds(value: number);
|
|
162
|
+
/**
|
|
163
|
+
* Creates a resource server from the given {@linkcode ResourceServerOptions}.
|
|
164
|
+
*
|
|
165
|
+
* @throws {RangeError} If `clockSkewSeconds` is negative, non-finite, or
|
|
166
|
+
* overflows when converted to milliseconds.
|
|
167
|
+
*/
|
|
150
168
|
constructor(options: ResourceServerOptions<Client, User, S>);
|
|
151
169
|
/**
|
|
152
170
|
* Builds a WWW-Authenticate header value per RFC 6750 Section 3.
|
|
@@ -10,6 +10,7 @@ import "../_dnt.polyfills.js";
|
|
|
10
10
|
import { BasicScope } from "../models/scope.js";
|
|
11
11
|
import { AccessDeniedError, InsufficientPermissionsError, InsufficientScopeError, InvalidTokenError, toOAuth2Error, } from "../errors.js";
|
|
12
12
|
import { authorizationFromClaims, } from "../models/authorization.js";
|
|
13
|
+
import { assertClockSkewSeconds } from "./_clock-skew.js";
|
|
13
14
|
/** Bearer token regex pattern. */
|
|
14
15
|
export const BEARER_TOKEN = /^ *(?:[Bb][Ee][Aa][Rr][Ee][Rr]) +([\w-.~+/]+=*) *$/;
|
|
15
16
|
const FAILURE_LABELS = {
|
|
@@ -73,15 +74,37 @@ export class ResourceServer {
|
|
|
73
74
|
Scope;
|
|
74
75
|
/** Protection space named in `WWW-Authenticate` challenges. */
|
|
75
76
|
realm;
|
|
76
|
-
/** Leeway in seconds applied to the access token's expiry. Defaults to `0`. */
|
|
77
|
-
clockSkewSeconds;
|
|
78
77
|
/** Body format used for error responses: OAuth2 or RFC 9457 problem details. */
|
|
79
78
|
errorFormat;
|
|
80
79
|
/** When true, handler boundaries rethrow {@linkcode OAuth2Error} instead of returning a response. */
|
|
81
80
|
throwOnError;
|
|
82
81
|
/** Resolves the per-request {@link ResourceServerContext}. */
|
|
83
82
|
resolveContext;
|
|
84
|
-
|
|
83
|
+
#clockSkewSeconds = 0;
|
|
84
|
+
/**
|
|
85
|
+
* Leeway in seconds applied to the access token's expiry. Defaults to `0`.
|
|
86
|
+
* May be reassigned to any non-negative finite number with a finite
|
|
87
|
+
* millisecond conversion, including fractional seconds.
|
|
88
|
+
*/
|
|
89
|
+
get clockSkewSeconds() {
|
|
90
|
+
return this.#clockSkewSeconds;
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* Sets the expiry leeway, preserving the previous value if validation fails.
|
|
94
|
+
*
|
|
95
|
+
* @throws {RangeError} If the value is negative, non-finite, or overflows
|
|
96
|
+
* when converted to milliseconds.
|
|
97
|
+
*/
|
|
98
|
+
set clockSkewSeconds(value) {
|
|
99
|
+
assertClockSkewSeconds(value);
|
|
100
|
+
this.#clockSkewSeconds = value;
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Creates a resource server from the given {@linkcode ResourceServerOptions}.
|
|
104
|
+
*
|
|
105
|
+
* @throws {RangeError} If `clockSkewSeconds` is negative, non-finite, or
|
|
106
|
+
* overflows when converted to milliseconds.
|
|
107
|
+
*/
|
|
85
108
|
constructor(options) {
|
|
86
109
|
this.Scope = options.Scope ??
|
|
87
110
|
BasicScope;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@udibo/oauth2",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "A flexible OAuth2 toolkit: framework-agnostic authorization & resource servers, a typed client, Hono adapters (including a backend-for-frontend), and React bindings.",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|