@auth0/auth0-server-js 1.3.0 → 1.4.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.
- package/README.md +6 -1
- package/dist/index.cjs +249 -55
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +54 -8
- package/dist/index.d.ts +54 -8
- package/dist/index.js +244 -51
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -49,7 +49,8 @@ const auth0 = new ServerClient<StoreOptions>({
|
|
|
49
49
|
```
|
|
50
50
|
|
|
51
51
|
The `AUTH0_DOMAIN`, `AUTH0_CLIENT_ID`, and `AUTH0_CLIENT_SECRET` can be obtained from the [Auth0 Dashboard](https://manage.auth0.com) once you've created an application. **This application must be a `Regular Web Application`**.
|
|
52
|
-
The `AUTH0_REDIRECT_URI` is needed to tell Auth0 what URL to redirect back to after
|
|
52
|
+
The `AUTH0_REDIRECT_URI` is needed to tell Auth0 what URL to redirect back to after successful authentication, e.g. `http://localhost:3000/auth/callback`. Your application needs to handle this endpoint and call the SDK's `completeInteractiveLogin(url: string)` to finish the authentication process. See below for more information.
|
|
53
|
+
|
|
53
54
|
|
|
54
55
|
### 3. Configuring the Store
|
|
55
56
|
|
|
@@ -200,6 +201,10 @@ const auth0 = new ServerClient<StoreOptions>({
|
|
|
200
201
|
});
|
|
201
202
|
```
|
|
202
203
|
|
|
204
|
+
The `redirect_uri` must be an absolute URL.
|
|
205
|
+
|
|
206
|
+
If you need to compute an absolute redirect URI per request (for example, in multi-domain deployments), pass `authorizationParams.redirect_uri` to `startInteractiveLogin` to override the configured default.
|
|
207
|
+
|
|
203
208
|
> [!IMPORTANT]
|
|
204
209
|
> You will need to register the `AUTH0_REDIRECT_URI` in your Auth0 Application as an **Allowed Callback URLs** via the [Auth0 Dashboard](https://manage.auth0.com):
|
|
205
210
|
|
package/dist/index.cjs
CHANGED
|
@@ -25,6 +25,7 @@ __export(index_exports, {
|
|
|
25
25
|
BackchannelLogoutError: () => BackchannelLogoutError,
|
|
26
26
|
CookieTransactionStore: () => CookieTransactionStore,
|
|
27
27
|
InvalidConfigurationError: () => InvalidConfigurationError,
|
|
28
|
+
IssuerValidationError: () => IssuerValidationError,
|
|
28
29
|
MissingRequiredArgumentError: () => MissingRequiredArgumentError,
|
|
29
30
|
MissingSessionError: () => MissingSessionError,
|
|
30
31
|
MissingTransactionError: () => MissingTransactionError,
|
|
@@ -78,6 +79,13 @@ var InvalidConfigurationError = class extends Error {
|
|
|
78
79
|
this.name = "InvalidConfigurationError";
|
|
79
80
|
}
|
|
80
81
|
};
|
|
82
|
+
var IssuerValidationError = class extends Error {
|
|
83
|
+
code = "issuer_validation_error";
|
|
84
|
+
constructor(message) {
|
|
85
|
+
super(message);
|
|
86
|
+
this.name = "IssuerValidationError";
|
|
87
|
+
}
|
|
88
|
+
};
|
|
81
89
|
|
|
82
90
|
// src/state/utils.ts
|
|
83
91
|
var createUpdatedTokenSet = (audience, response) => ({
|
|
@@ -86,7 +94,7 @@ var createUpdatedTokenSet = (audience, response) => ({
|
|
|
86
94
|
scope: response.scope,
|
|
87
95
|
expiresAt: response.expiresAt
|
|
88
96
|
});
|
|
89
|
-
function updateStateData(audience, stateData, tokenEndpointResponse) {
|
|
97
|
+
function updateStateData(audience, stateData, tokenEndpointResponse, context) {
|
|
90
98
|
if (stateData) {
|
|
91
99
|
const isNewTokenSet = !stateData.tokenSets.some(
|
|
92
100
|
(tokenSet) => tokenSet.audience === audience && tokenSet.scope === tokenEndpointResponse.scope
|
|
@@ -98,7 +106,8 @@ function updateStateData(audience, stateData, tokenEndpointResponse) {
|
|
|
98
106
|
...stateData,
|
|
99
107
|
idToken: tokenEndpointResponse.idToken ?? stateData.idToken,
|
|
100
108
|
refreshToken: tokenEndpointResponse.refreshToken ?? stateData.refreshToken,
|
|
101
|
-
tokenSets
|
|
109
|
+
tokenSets,
|
|
110
|
+
domain: context?.domain ?? stateData.domain
|
|
102
111
|
};
|
|
103
112
|
} else {
|
|
104
113
|
const user = tokenEndpointResponse.claims;
|
|
@@ -106,9 +115,8 @@ function updateStateData(audience, stateData, tokenEndpointResponse) {
|
|
|
106
115
|
user,
|
|
107
116
|
idToken: tokenEndpointResponse.idToken,
|
|
108
117
|
refreshToken: tokenEndpointResponse.refreshToken,
|
|
109
|
-
tokenSets: [
|
|
110
|
-
|
|
111
|
-
],
|
|
118
|
+
tokenSets: [createUpdatedTokenSet(audience, tokenEndpointResponse)],
|
|
119
|
+
domain: context?.domain,
|
|
112
120
|
internal: {
|
|
113
121
|
sid: user?.sid,
|
|
114
122
|
createdAt: Math.floor(Date.now() / 1e3)
|
|
@@ -153,6 +161,9 @@ var compareScopes = (scopes, requiredScopes) => {
|
|
|
153
161
|
return requiredScopesArray.every((scope) => scopesSet.has(scope));
|
|
154
162
|
};
|
|
155
163
|
|
|
164
|
+
// src/server-client.ts
|
|
165
|
+
var import_jose = require("jose");
|
|
166
|
+
|
|
156
167
|
// src/telemetry.ts
|
|
157
168
|
function getTelemetryConfig(config) {
|
|
158
169
|
if (config?.enabled === false) {
|
|
@@ -161,13 +172,26 @@ function getTelemetryConfig(config) {
|
|
|
161
172
|
return {
|
|
162
173
|
enabled: true,
|
|
163
174
|
name: config?.name ?? "@auth0/auth0-server-js",
|
|
164
|
-
version: config?.version ?? "1.
|
|
175
|
+
version: config?.version ?? "1.4.0"
|
|
165
176
|
};
|
|
166
177
|
}
|
|
167
178
|
|
|
168
179
|
// src/server-client.ts
|
|
169
180
|
var DEFAULT_SCOPES = "openid profile email offline_access";
|
|
170
|
-
|
|
181
|
+
var normalizeDomain = (value) => {
|
|
182
|
+
const trimmed = value.trim();
|
|
183
|
+
const parsed = trimmed.startsWith("http") ? new URL(trimmed) : new URL(`https://${trimmed}`);
|
|
184
|
+
return parsed.host.toLowerCase();
|
|
185
|
+
};
|
|
186
|
+
var decodeIssuer = (token) => {
|
|
187
|
+
try {
|
|
188
|
+
const { iss } = (0, import_jose.decodeJwt)(token);
|
|
189
|
+
return typeof iss === "string" ? iss : void 0;
|
|
190
|
+
} catch {
|
|
191
|
+
return void 0;
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
var ensureOpenIdScope = (scope) => {
|
|
171
195
|
if (!scope) {
|
|
172
196
|
return DEFAULT_SCOPES;
|
|
173
197
|
}
|
|
@@ -176,20 +200,32 @@ function ensureOpenIdScope(scope) {
|
|
|
176
200
|
scopes.unshift("openid");
|
|
177
201
|
}
|
|
178
202
|
return scopes.join(" ");
|
|
179
|
-
}
|
|
203
|
+
};
|
|
180
204
|
var ServerClient = class {
|
|
181
205
|
#options;
|
|
182
206
|
#transactionStore;
|
|
183
207
|
#transactionStoreIdentifier;
|
|
184
208
|
#stateStore;
|
|
185
209
|
#stateStoreIdentifier;
|
|
210
|
+
#authClientOptions;
|
|
211
|
+
#staticDomain;
|
|
212
|
+
#authClient;
|
|
186
213
|
/**
|
|
187
214
|
* The underlying `authClient` instance that can be used to interact with the Auth0 Authentication API.
|
|
188
215
|
* Generally, you should prefer to use the higher-level methods exposed on the `ServerClient` instance.
|
|
189
|
-
*
|
|
216
|
+
*
|
|
217
|
+
* This property can only be used when `domain` is configured as a static string.
|
|
218
|
+
* In resolver mode (`domain` as a function), the SDK resolves the domain per request,
|
|
219
|
+
* so use `ServerClient` methods instead.
|
|
220
|
+
*
|
|
190
221
|
* Important: the methods exposed on the `authClient` instance do not handle any session or state management.
|
|
191
222
|
*/
|
|
192
|
-
authClient
|
|
223
|
+
get authClient() {
|
|
224
|
+
if (!this.#authClient) {
|
|
225
|
+
throw new InvalidConfigurationError("authClient is only available when using a static domain configuration.");
|
|
226
|
+
}
|
|
227
|
+
return this.#authClient;
|
|
228
|
+
}
|
|
193
229
|
constructor(options) {
|
|
194
230
|
this.#options = options;
|
|
195
231
|
this.#stateStoreIdentifier = this.#options.stateIdentifier || "__a0_session";
|
|
@@ -202,18 +238,77 @@ var ServerClient = class {
|
|
|
202
238
|
if (!this.#options.transactionStore) {
|
|
203
239
|
throw new MissingRequiredArgumentError("transactionStore");
|
|
204
240
|
}
|
|
205
|
-
this.
|
|
206
|
-
domain
|
|
241
|
+
if (typeof this.#options.domain !== "string" && typeof this.#options.domain !== "function") {
|
|
242
|
+
throw new InvalidConfigurationError("domain must be a string or resolver function");
|
|
243
|
+
}
|
|
244
|
+
this.#authClientOptions = {
|
|
207
245
|
clientId: this.#options.clientId,
|
|
208
246
|
clientSecret: this.#options.clientSecret,
|
|
209
247
|
clientAssertionSigningKey: this.#options.clientAssertionSigningKey,
|
|
210
248
|
clientAssertionSigningAlg: this.#options.clientAssertionSigningAlg,
|
|
211
249
|
authorizationParams: this.#options.authorizationParams,
|
|
250
|
+
discoveryCache: this.#options.discoveryCache,
|
|
212
251
|
customFetch: this.#options.customFetch,
|
|
213
|
-
useMtls: this.#options.useMtls
|
|
252
|
+
useMtls: this.#options.useMtls
|
|
253
|
+
};
|
|
254
|
+
if (typeof this.#options.domain === "string") {
|
|
255
|
+
const domain = normalizeDomain(this.#options.domain);
|
|
256
|
+
this.#staticDomain = domain;
|
|
257
|
+
this.#authClient = new import_auth0_auth_js.AuthClient({
|
|
258
|
+
domain,
|
|
259
|
+
...this.#authClientOptions,
|
|
260
|
+
telemetry: getTelemetryConfig(this.#options.telemetry)
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
async #resolveDomain(storeOptions) {
|
|
265
|
+
if (typeof this.#options.domain === "function") {
|
|
266
|
+
const resolved = await this.#options.domain(storeOptions);
|
|
267
|
+
if (typeof resolved !== "string" || resolved.trim().length === 0) {
|
|
268
|
+
throw new InvalidConfigurationError("domainResolver returned no domain");
|
|
269
|
+
}
|
|
270
|
+
return normalizeDomain(resolved);
|
|
271
|
+
}
|
|
272
|
+
return normalizeDomain(this.#options.domain);
|
|
273
|
+
}
|
|
274
|
+
#createAuthClient(domain) {
|
|
275
|
+
return new import_auth0_auth_js.AuthClient({
|
|
276
|
+
domain,
|
|
277
|
+
...this.#authClientOptions,
|
|
214
278
|
telemetry: getTelemetryConfig(this.#options.telemetry)
|
|
215
279
|
});
|
|
216
280
|
}
|
|
281
|
+
#getAuthClient(domain) {
|
|
282
|
+
const normalizedDomain = normalizeDomain(domain);
|
|
283
|
+
if (this.#authClient && this.#staticDomain === normalizedDomain) {
|
|
284
|
+
return this.#authClient;
|
|
285
|
+
}
|
|
286
|
+
return this.#createAuthClient(normalizedDomain);
|
|
287
|
+
}
|
|
288
|
+
#getSessionDomain(stateData) {
|
|
289
|
+
if (stateData.domain) {
|
|
290
|
+
return normalizeDomain(stateData.domain);
|
|
291
|
+
}
|
|
292
|
+
if (this.#staticDomain) {
|
|
293
|
+
return this.#staticDomain;
|
|
294
|
+
}
|
|
295
|
+
const issuerFromClaims = stateData.user?.iss;
|
|
296
|
+
if (typeof issuerFromClaims === "string" && issuerFromClaims.trim().length > 0) {
|
|
297
|
+
return normalizeDomain(issuerFromClaims);
|
|
298
|
+
}
|
|
299
|
+
return;
|
|
300
|
+
}
|
|
301
|
+
#isResolverMode() {
|
|
302
|
+
return typeof this.#options.domain === "function";
|
|
303
|
+
}
|
|
304
|
+
async #isSessionForCurrentDomain(stateData, storeOptions) {
|
|
305
|
+
const sessionDomain = this.#getSessionDomain(stateData);
|
|
306
|
+
if (!sessionDomain) {
|
|
307
|
+
return false;
|
|
308
|
+
}
|
|
309
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
310
|
+
return sessionDomain === resolvedDomain;
|
|
311
|
+
}
|
|
217
312
|
/**
|
|
218
313
|
* Starts the interactive login process, and returns a URL to redirect the user-agent to to request authorization at Auth0.
|
|
219
314
|
* @param options Optional options used to configure the interactive login process.
|
|
@@ -229,7 +324,9 @@ var ServerClient = class {
|
|
|
229
324
|
throw new MissingRequiredArgumentError("authorizationParams.redirect_uri");
|
|
230
325
|
}
|
|
231
326
|
const scope = ensureOpenIdScope(options?.authorizationParams?.scope ?? this.#options.authorizationParams?.scope);
|
|
232
|
-
const
|
|
327
|
+
const domain = await this.#resolveDomain(storeOptions);
|
|
328
|
+
const authClient = this.#getAuthClient(domain);
|
|
329
|
+
const { codeVerifier, authorizationUrl } = await authClient.buildAuthorizationUrl({
|
|
233
330
|
pushedAuthorizationRequests: options?.pushedAuthorizationRequests,
|
|
234
331
|
authorizationParams: {
|
|
235
332
|
...options?.authorizationParams,
|
|
@@ -239,7 +336,8 @@ var ServerClient = class {
|
|
|
239
336
|
});
|
|
240
337
|
const transactionState = {
|
|
241
338
|
audience: options?.authorizationParams?.audience ?? this.#options.authorizationParams?.audience,
|
|
242
|
-
codeVerifier
|
|
339
|
+
codeVerifier,
|
|
340
|
+
domain
|
|
243
341
|
};
|
|
244
342
|
if (options?.appState) {
|
|
245
343
|
transactionState.appState = options.appState;
|
|
@@ -263,11 +361,15 @@ var ServerClient = class {
|
|
|
263
361
|
if (!transactionData) {
|
|
264
362
|
throw new MissingTransactionError();
|
|
265
363
|
}
|
|
266
|
-
const
|
|
364
|
+
const domain = transactionData.domain ?? await this.#resolveDomain(storeOptions);
|
|
365
|
+
const authClient = this.#getAuthClient(domain);
|
|
366
|
+
const tokenEndpointResponse = await authClient.getTokenByCode(url, {
|
|
267
367
|
codeVerifier: transactionData.codeVerifier
|
|
268
368
|
});
|
|
269
369
|
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
270
|
-
const stateData = updateStateData(transactionData.audience ?? "default", existingStateData, tokenEndpointResponse
|
|
370
|
+
const stateData = updateStateData(transactionData.audience ?? "default", existingStateData, tokenEndpointResponse, {
|
|
371
|
+
domain
|
|
372
|
+
});
|
|
271
373
|
await this.#stateStore.set(this.#stateStoreIdentifier, stateData, true, storeOptions);
|
|
272
374
|
await this.#transactionStore.delete(this.#transactionStoreIdentifier, storeOptions);
|
|
273
375
|
return { appState: transactionData.appState, authorizationDetails: tokenEndpointResponse.authorizationDetails };
|
|
@@ -289,7 +391,15 @@ var ServerClient = class {
|
|
|
289
391
|
"Unable to start the user linking process without a logged in user. Ensure to login using the SDK before starting the user linking process."
|
|
290
392
|
);
|
|
291
393
|
}
|
|
292
|
-
|
|
394
|
+
if (this.#isResolverMode()) {
|
|
395
|
+
const isCurrentDomain = await this.#isSessionForCurrentDomain(stateData, storeOptions);
|
|
396
|
+
if (!isCurrentDomain) {
|
|
397
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
398
|
+
}
|
|
399
|
+
}
|
|
400
|
+
const domain = this.#getSessionDomain(stateData);
|
|
401
|
+
const authClient = this.#getAuthClient(domain);
|
|
402
|
+
const { linkUserUrl, codeVerifier } = await authClient.buildLinkUserUrl({
|
|
293
403
|
connection: options.connection,
|
|
294
404
|
connectionScope: options.connectionScope,
|
|
295
405
|
idToken: stateData.idToken,
|
|
@@ -297,7 +407,8 @@ var ServerClient = class {
|
|
|
297
407
|
});
|
|
298
408
|
const transactionState = {
|
|
299
409
|
audience: options?.authorizationParams?.audience ?? this.#options.authorizationParams?.audience,
|
|
300
|
-
codeVerifier
|
|
410
|
+
codeVerifier,
|
|
411
|
+
domain
|
|
301
412
|
};
|
|
302
413
|
if (options?.appState) {
|
|
303
414
|
transactionState.appState = options.appState;
|
|
@@ -339,14 +450,23 @@ var ServerClient = class {
|
|
|
339
450
|
"Unable to start the user unlinking process without a logged in user. Ensure to login using the SDK before starting the user unlinking process."
|
|
340
451
|
);
|
|
341
452
|
}
|
|
342
|
-
|
|
453
|
+
if (this.#isResolverMode()) {
|
|
454
|
+
const isCurrentDomain = await this.#isSessionForCurrentDomain(stateData, storeOptions);
|
|
455
|
+
if (!isCurrentDomain) {
|
|
456
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
const domain = this.#getSessionDomain(stateData);
|
|
460
|
+
const authClient = this.#getAuthClient(domain);
|
|
461
|
+
const { unlinkUserUrl, codeVerifier } = await authClient.buildUnlinkUserUrl({
|
|
343
462
|
connection: options.connection,
|
|
344
463
|
idToken: stateData.idToken,
|
|
345
464
|
authorizationParams: options.authorizationParams
|
|
346
465
|
});
|
|
347
466
|
const transactionState = {
|
|
348
467
|
audience: options?.authorizationParams?.audience ?? this.#options.authorizationParams?.audience,
|
|
349
|
-
codeVerifier
|
|
468
|
+
codeVerifier,
|
|
469
|
+
domain
|
|
350
470
|
};
|
|
351
471
|
if (options?.appState) {
|
|
352
472
|
transactionState.appState = options.appState;
|
|
@@ -385,7 +505,9 @@ var ServerClient = class {
|
|
|
385
505
|
*/
|
|
386
506
|
async loginBackchannel(options, storeOptions) {
|
|
387
507
|
const scope = ensureOpenIdScope(options.authorizationParams?.scope ?? this.#options.authorizationParams?.scope);
|
|
388
|
-
const
|
|
508
|
+
const domain = await this.#resolveDomain(storeOptions);
|
|
509
|
+
const authClient = this.#getAuthClient(domain);
|
|
510
|
+
const tokenEndpointResponse = await authClient.backchannelAuthentication({
|
|
389
511
|
bindingMessage: options.bindingMessage,
|
|
390
512
|
loginHint: options.loginHint,
|
|
391
513
|
authorizationParams: {
|
|
@@ -397,7 +519,8 @@ var ServerClient = class {
|
|
|
397
519
|
const stateData = updateStateData(
|
|
398
520
|
this.#options.authorizationParams?.audience ?? "default",
|
|
399
521
|
existingStateData,
|
|
400
|
-
tokenEndpointResponse
|
|
522
|
+
tokenEndpointResponse,
|
|
523
|
+
{ domain }
|
|
401
524
|
);
|
|
402
525
|
await this.#stateStore.set(this.#stateStoreIdentifier, stateData, true, storeOptions);
|
|
403
526
|
return {
|
|
@@ -411,16 +534,31 @@ var ServerClient = class {
|
|
|
411
534
|
*/
|
|
412
535
|
async getUser(storeOptions) {
|
|
413
536
|
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
414
|
-
|
|
537
|
+
if (!stateData) {
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
if (this.#isResolverMode()) {
|
|
541
|
+
const isCurrentDomain = await this.#isSessionForCurrentDomain(stateData, storeOptions);
|
|
542
|
+
if (!isCurrentDomain) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
return stateData.user;
|
|
415
547
|
}
|
|
416
548
|
/**
|
|
417
549
|
* Retrieve the user session from the store, or undefined if no session found.
|
|
418
550
|
* @param storeOptions Optional options used to pass to the Transaction and State Store.
|
|
419
|
-
* @returns The
|
|
551
|
+
* @returns The session or undefined if no session found in the store.
|
|
420
552
|
*/
|
|
421
553
|
async getSession(storeOptions) {
|
|
422
554
|
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
423
555
|
if (stateData) {
|
|
556
|
+
if (this.#isResolverMode()) {
|
|
557
|
+
const isCurrentDomain = await this.#isSessionForCurrentDomain(stateData, storeOptions);
|
|
558
|
+
if (!isCurrentDomain) {
|
|
559
|
+
return;
|
|
560
|
+
}
|
|
561
|
+
}
|
|
424
562
|
const { internal, ...sessionData } = stateData;
|
|
425
563
|
return sessionData;
|
|
426
564
|
}
|
|
@@ -428,24 +566,29 @@ var ServerClient = class {
|
|
|
428
566
|
/**
|
|
429
567
|
* Retrieves the access token from the store, or calls Auth0 when the access token is expired and a refresh token is available in the store.
|
|
430
568
|
* Also updates the store when a new token was retrieved from Auth0.
|
|
431
|
-
*
|
|
432
|
-
* @param options Optional options for requesting specific audience/scope.
|
|
433
569
|
* @param storeOptions Optional options used to pass to the Transaction and State Store.
|
|
434
570
|
*
|
|
435
571
|
* @throws {TokenByRefreshTokenError} If the refresh token was not found or there was an issue requesting the access token.
|
|
436
572
|
*
|
|
437
573
|
* @returns The Token Set, containing the access token, as well as additional information.
|
|
438
574
|
*/
|
|
439
|
-
async getAccessToken(
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
)
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
575
|
+
async getAccessToken(storeOptions) {
|
|
576
|
+
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
577
|
+
const audience = this.#options.authorizationParams?.audience ?? "default";
|
|
578
|
+
const scope = this.#options.authorizationParams?.scope;
|
|
579
|
+
const sessionDomain = stateData ? this.#getSessionDomain(stateData) : this.#staticDomain;
|
|
580
|
+
if (this.#isResolverMode()) {
|
|
581
|
+
if (!stateData) {
|
|
582
|
+
throw new MissingSessionError("Unable to retrieve access token without a logged in user.");
|
|
583
|
+
}
|
|
584
|
+
if (!sessionDomain) {
|
|
585
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
586
|
+
}
|
|
587
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
588
|
+
if (sessionDomain !== resolvedDomain) {
|
|
589
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
590
|
+
}
|
|
591
|
+
}
|
|
449
592
|
const tokenSet = stateData?.tokenSets.find(
|
|
450
593
|
(tokenSet2) => tokenSet2.audience === audience && (!scope || compareScopes(tokenSet2.scope, scope))
|
|
451
594
|
);
|
|
@@ -457,14 +600,15 @@ var ServerClient = class {
|
|
|
457
600
|
"The access token has expired and a refresh token was not provided. The user needs to re-authenticate."
|
|
458
601
|
);
|
|
459
602
|
}
|
|
460
|
-
const
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
};
|
|
464
|
-
const
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
603
|
+
const domainForSession = sessionDomain;
|
|
604
|
+
const tokenEndpointResponse = await this.#getAuthClient(domainForSession).getTokenByRefreshToken({
|
|
605
|
+
refreshToken: stateData.refreshToken
|
|
606
|
+
});
|
|
607
|
+
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
608
|
+
const updatedStateData = updateStateData(audience, existingStateData, tokenEndpointResponse, {
|
|
609
|
+
domain: domainForSession
|
|
610
|
+
});
|
|
611
|
+
await this.#stateStore.set(this.#stateStoreIdentifier, updatedStateData, false, storeOptions);
|
|
468
612
|
return {
|
|
469
613
|
accessToken: tokenEndpointResponse.accessToken,
|
|
470
614
|
scope: tokenEndpointResponse.scope,
|
|
@@ -489,6 +633,19 @@ var ServerClient = class {
|
|
|
489
633
|
*/
|
|
490
634
|
async getAccessTokenForConnection(options, storeOptions) {
|
|
491
635
|
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
636
|
+
const sessionDomain = stateData ? this.#getSessionDomain(stateData) : this.#staticDomain;
|
|
637
|
+
if (this.#isResolverMode()) {
|
|
638
|
+
if (!stateData) {
|
|
639
|
+
throw new MissingSessionError("Unable to retrieve an access token for a connection without a logged in user.");
|
|
640
|
+
}
|
|
641
|
+
if (!sessionDomain) {
|
|
642
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
643
|
+
}
|
|
644
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
645
|
+
if (sessionDomain !== resolvedDomain) {
|
|
646
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
647
|
+
}
|
|
648
|
+
}
|
|
492
649
|
const connectionTokenSet = stateData?.connectionTokenSets?.find(
|
|
493
650
|
(tokenSet) => tokenSet.connection === options.connection
|
|
494
651
|
);
|
|
@@ -500,12 +657,20 @@ var ServerClient = class {
|
|
|
500
657
|
"A refresh token was not found but is required to be able to retrieve an access token for a connection."
|
|
501
658
|
);
|
|
502
659
|
}
|
|
503
|
-
const
|
|
660
|
+
const domainForSession = sessionDomain;
|
|
661
|
+
const tokenEndpointResponse = await this.#getAuthClient(domainForSession).getTokenForConnection({
|
|
504
662
|
connection: options.connection,
|
|
505
663
|
loginHint: options.loginHint,
|
|
506
664
|
refreshToken: stateData.refreshToken
|
|
507
665
|
});
|
|
508
|
-
const updatedStateData = updateStateDataForConnectionTokenSet(
|
|
666
|
+
const updatedStateData = updateStateDataForConnectionTokenSet(
|
|
667
|
+
options,
|
|
668
|
+
{
|
|
669
|
+
...stateData,
|
|
670
|
+
domain: stateData.domain ?? domainForSession
|
|
671
|
+
},
|
|
672
|
+
tokenEndpointResponse
|
|
673
|
+
);
|
|
509
674
|
await this.#stateStore.set(this.#stateStoreIdentifier, updatedStateData, false, storeOptions);
|
|
510
675
|
return {
|
|
511
676
|
accessToken: tokenEndpointResponse.accessToken,
|
|
@@ -522,8 +687,21 @@ var ServerClient = class {
|
|
|
522
687
|
* @returns {URL}
|
|
523
688
|
*/
|
|
524
689
|
async logout(options, storeOptions) {
|
|
525
|
-
|
|
526
|
-
|
|
690
|
+
if (!this.#isResolverMode()) {
|
|
691
|
+
await this.#stateStore.delete(this.#stateStoreIdentifier, storeOptions);
|
|
692
|
+
return this.authClient.buildLogoutUrl(options);
|
|
693
|
+
}
|
|
694
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
695
|
+
const authClient = this.#getAuthClient(resolvedDomain);
|
|
696
|
+
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
697
|
+
const sessionDomain = stateData ? this.#getSessionDomain(stateData) : void 0;
|
|
698
|
+
if (!stateData) {
|
|
699
|
+
return authClient.buildLogoutUrl(options);
|
|
700
|
+
}
|
|
701
|
+
if (sessionDomain && sessionDomain === resolvedDomain) {
|
|
702
|
+
await this.#stateStore.delete(this.#stateStoreIdentifier, storeOptions);
|
|
703
|
+
}
|
|
704
|
+
return authClient.buildLogoutUrl(options);
|
|
527
705
|
}
|
|
528
706
|
/**
|
|
529
707
|
* Handles the backchannel logout process by verifying the logout token and deleting the session from the store if the logout token was considered valid.
|
|
@@ -537,13 +715,28 @@ var ServerClient = class {
|
|
|
537
715
|
if (!logoutToken) {
|
|
538
716
|
throw new BackchannelLogoutError("Missing Logout Token");
|
|
539
717
|
}
|
|
540
|
-
|
|
541
|
-
|
|
718
|
+
if (!this.#isResolverMode()) {
|
|
719
|
+
const logoutTokenClaims2 = await this.authClient.verifyLogoutToken({ logoutToken });
|
|
720
|
+
await this.#stateStore.deleteByLogoutToken(logoutTokenClaims2, storeOptions);
|
|
721
|
+
return;
|
|
722
|
+
}
|
|
723
|
+
const issuer = decodeIssuer(logoutToken);
|
|
724
|
+
if (!issuer) {
|
|
725
|
+
throw new BackchannelLogoutError("Logout token is missing an issuer");
|
|
726
|
+
}
|
|
727
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
728
|
+
const domain = normalizeDomain(issuer);
|
|
729
|
+
if (domain !== resolvedDomain) {
|
|
730
|
+
throw new BackchannelLogoutError("Logout token issuer does not match the resolved domain");
|
|
731
|
+
}
|
|
732
|
+
const authClient = this.#getAuthClient(domain);
|
|
733
|
+
const logoutTokenClaims = await authClient.verifyLogoutToken({ logoutToken });
|
|
734
|
+
await this.#stateStore.deleteByLogoutToken({ ...logoutTokenClaims, iss: issuer }, storeOptions);
|
|
542
735
|
}
|
|
543
736
|
};
|
|
544
737
|
|
|
545
738
|
// src/encryption/index.ts
|
|
546
|
-
var
|
|
739
|
+
var import_jose2 = require("jose");
|
|
547
740
|
var ENC = "A256CBC-HS512";
|
|
548
741
|
var ALG = "dir";
|
|
549
742
|
var DIGEST = "SHA-256";
|
|
@@ -551,7 +744,7 @@ var BIT_LENGTH = 512;
|
|
|
551
744
|
var HKDF_INFO = "derived cookie encryption secret";
|
|
552
745
|
var encoder;
|
|
553
746
|
function isDecryptionError(e) {
|
|
554
|
-
return e instanceof
|
|
747
|
+
return e instanceof import_jose2.errors.JWEDecryptionFailed || e instanceof import_jose2.errors.JWEInvalid;
|
|
555
748
|
}
|
|
556
749
|
async function deriveEncryptionSecret(secret, salt, kid) {
|
|
557
750
|
encoder ||= new TextEncoder();
|
|
@@ -572,7 +765,7 @@ async function deriveEncryptionSecret(secret, salt, kid) {
|
|
|
572
765
|
async function _encrypt(payload, secret, salt, expiration) {
|
|
573
766
|
const kid = crypto.randomUUID();
|
|
574
767
|
const encryptionSecret = await deriveEncryptionSecret(secret, salt, kid);
|
|
575
|
-
return await new
|
|
768
|
+
return await new import_jose2.EncryptJWT(payload).setProtectedHeader({ enc: ENC, alg: ALG, kid }).setExpirationTime(expiration).encrypt(encryptionSecret);
|
|
576
769
|
}
|
|
577
770
|
async function encrypt(payload, secret, salt, expiration) {
|
|
578
771
|
if (typeof secret === "string") {
|
|
@@ -586,7 +779,7 @@ async function encrypt(payload, secret, salt, expiration) {
|
|
|
586
779
|
}
|
|
587
780
|
}
|
|
588
781
|
async function _decrypt(value, secret, salt) {
|
|
589
|
-
const res = await (0,
|
|
782
|
+
const res = await (0, import_jose2.jwtDecrypt)(
|
|
590
783
|
value,
|
|
591
784
|
async (protectedHeader) => {
|
|
592
785
|
if (!protectedHeader.kid) {
|
|
@@ -863,6 +1056,7 @@ var StatelessStateStore = class extends AbstractSessionStore {
|
|
|
863
1056
|
BackchannelLogoutError,
|
|
864
1057
|
CookieTransactionStore,
|
|
865
1058
|
InvalidConfigurationError,
|
|
1059
|
+
IssuerValidationError,
|
|
866
1060
|
MissingRequiredArgumentError,
|
|
867
1061
|
MissingSessionError,
|
|
868
1062
|
MissingTransactionError,
|