@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/dist/index.js
CHANGED
|
@@ -41,6 +41,13 @@ var InvalidConfigurationError = class extends Error {
|
|
|
41
41
|
this.name = "InvalidConfigurationError";
|
|
42
42
|
}
|
|
43
43
|
};
|
|
44
|
+
var IssuerValidationError = class extends Error {
|
|
45
|
+
code = "issuer_validation_error";
|
|
46
|
+
constructor(message) {
|
|
47
|
+
super(message);
|
|
48
|
+
this.name = "IssuerValidationError";
|
|
49
|
+
}
|
|
50
|
+
};
|
|
44
51
|
|
|
45
52
|
// src/state/utils.ts
|
|
46
53
|
var createUpdatedTokenSet = (audience, response) => ({
|
|
@@ -49,7 +56,7 @@ var createUpdatedTokenSet = (audience, response) => ({
|
|
|
49
56
|
scope: response.scope,
|
|
50
57
|
expiresAt: response.expiresAt
|
|
51
58
|
});
|
|
52
|
-
function updateStateData(audience, stateData, tokenEndpointResponse) {
|
|
59
|
+
function updateStateData(audience, stateData, tokenEndpointResponse, context) {
|
|
53
60
|
if (stateData) {
|
|
54
61
|
const isNewTokenSet = !stateData.tokenSets.some(
|
|
55
62
|
(tokenSet) => tokenSet.audience === audience && tokenSet.scope === tokenEndpointResponse.scope
|
|
@@ -61,7 +68,8 @@ function updateStateData(audience, stateData, tokenEndpointResponse) {
|
|
|
61
68
|
...stateData,
|
|
62
69
|
idToken: tokenEndpointResponse.idToken ?? stateData.idToken,
|
|
63
70
|
refreshToken: tokenEndpointResponse.refreshToken ?? stateData.refreshToken,
|
|
64
|
-
tokenSets
|
|
71
|
+
tokenSets,
|
|
72
|
+
domain: context?.domain ?? stateData.domain
|
|
65
73
|
};
|
|
66
74
|
} else {
|
|
67
75
|
const user = tokenEndpointResponse.claims;
|
|
@@ -69,9 +77,8 @@ function updateStateData(audience, stateData, tokenEndpointResponse) {
|
|
|
69
77
|
user,
|
|
70
78
|
idToken: tokenEndpointResponse.idToken,
|
|
71
79
|
refreshToken: tokenEndpointResponse.refreshToken,
|
|
72
|
-
tokenSets: [
|
|
73
|
-
|
|
74
|
-
],
|
|
80
|
+
tokenSets: [createUpdatedTokenSet(audience, tokenEndpointResponse)],
|
|
81
|
+
domain: context?.domain,
|
|
75
82
|
internal: {
|
|
76
83
|
sid: user?.sid,
|
|
77
84
|
createdAt: Math.floor(Date.now() / 1e3)
|
|
@@ -120,6 +127,9 @@ var compareScopes = (scopes, requiredScopes) => {
|
|
|
120
127
|
return requiredScopesArray.every((scope) => scopesSet.has(scope));
|
|
121
128
|
};
|
|
122
129
|
|
|
130
|
+
// src/server-client.ts
|
|
131
|
+
import { decodeJwt } from "jose";
|
|
132
|
+
|
|
123
133
|
// src/telemetry.ts
|
|
124
134
|
function getTelemetryConfig(config) {
|
|
125
135
|
if (config?.enabled === false) {
|
|
@@ -128,13 +138,26 @@ function getTelemetryConfig(config) {
|
|
|
128
138
|
return {
|
|
129
139
|
enabled: true,
|
|
130
140
|
name: config?.name ?? "@auth0/auth0-server-js",
|
|
131
|
-
version: config?.version ?? "1.
|
|
141
|
+
version: config?.version ?? "1.4.0"
|
|
132
142
|
};
|
|
133
143
|
}
|
|
134
144
|
|
|
135
145
|
// src/server-client.ts
|
|
136
146
|
var DEFAULT_SCOPES = "openid profile email offline_access";
|
|
137
|
-
|
|
147
|
+
var normalizeDomain = (value) => {
|
|
148
|
+
const trimmed = value.trim();
|
|
149
|
+
const parsed = trimmed.startsWith("http") ? new URL(trimmed) : new URL(`https://${trimmed}`);
|
|
150
|
+
return parsed.host.toLowerCase();
|
|
151
|
+
};
|
|
152
|
+
var decodeIssuer = (token) => {
|
|
153
|
+
try {
|
|
154
|
+
const { iss } = decodeJwt(token);
|
|
155
|
+
return typeof iss === "string" ? iss : void 0;
|
|
156
|
+
} catch {
|
|
157
|
+
return void 0;
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
var ensureOpenIdScope = (scope) => {
|
|
138
161
|
if (!scope) {
|
|
139
162
|
return DEFAULT_SCOPES;
|
|
140
163
|
}
|
|
@@ -143,20 +166,32 @@ function ensureOpenIdScope(scope) {
|
|
|
143
166
|
scopes.unshift("openid");
|
|
144
167
|
}
|
|
145
168
|
return scopes.join(" ");
|
|
146
|
-
}
|
|
169
|
+
};
|
|
147
170
|
var ServerClient = class {
|
|
148
171
|
#options;
|
|
149
172
|
#transactionStore;
|
|
150
173
|
#transactionStoreIdentifier;
|
|
151
174
|
#stateStore;
|
|
152
175
|
#stateStoreIdentifier;
|
|
176
|
+
#authClientOptions;
|
|
177
|
+
#staticDomain;
|
|
178
|
+
#authClient;
|
|
153
179
|
/**
|
|
154
180
|
* The underlying `authClient` instance that can be used to interact with the Auth0 Authentication API.
|
|
155
181
|
* Generally, you should prefer to use the higher-level methods exposed on the `ServerClient` instance.
|
|
156
|
-
*
|
|
182
|
+
*
|
|
183
|
+
* This property can only be used when `domain` is configured as a static string.
|
|
184
|
+
* In resolver mode (`domain` as a function), the SDK resolves the domain per request,
|
|
185
|
+
* so use `ServerClient` methods instead.
|
|
186
|
+
*
|
|
157
187
|
* Important: the methods exposed on the `authClient` instance do not handle any session or state management.
|
|
158
188
|
*/
|
|
159
|
-
authClient
|
|
189
|
+
get authClient() {
|
|
190
|
+
if (!this.#authClient) {
|
|
191
|
+
throw new InvalidConfigurationError("authClient is only available when using a static domain configuration.");
|
|
192
|
+
}
|
|
193
|
+
return this.#authClient;
|
|
194
|
+
}
|
|
160
195
|
constructor(options) {
|
|
161
196
|
this.#options = options;
|
|
162
197
|
this.#stateStoreIdentifier = this.#options.stateIdentifier || "__a0_session";
|
|
@@ -169,18 +204,77 @@ var ServerClient = class {
|
|
|
169
204
|
if (!this.#options.transactionStore) {
|
|
170
205
|
throw new MissingRequiredArgumentError("transactionStore");
|
|
171
206
|
}
|
|
172
|
-
this.
|
|
173
|
-
domain
|
|
207
|
+
if (typeof this.#options.domain !== "string" && typeof this.#options.domain !== "function") {
|
|
208
|
+
throw new InvalidConfigurationError("domain must be a string or resolver function");
|
|
209
|
+
}
|
|
210
|
+
this.#authClientOptions = {
|
|
174
211
|
clientId: this.#options.clientId,
|
|
175
212
|
clientSecret: this.#options.clientSecret,
|
|
176
213
|
clientAssertionSigningKey: this.#options.clientAssertionSigningKey,
|
|
177
214
|
clientAssertionSigningAlg: this.#options.clientAssertionSigningAlg,
|
|
178
215
|
authorizationParams: this.#options.authorizationParams,
|
|
216
|
+
discoveryCache: this.#options.discoveryCache,
|
|
179
217
|
customFetch: this.#options.customFetch,
|
|
180
|
-
useMtls: this.#options.useMtls
|
|
218
|
+
useMtls: this.#options.useMtls
|
|
219
|
+
};
|
|
220
|
+
if (typeof this.#options.domain === "string") {
|
|
221
|
+
const domain = normalizeDomain(this.#options.domain);
|
|
222
|
+
this.#staticDomain = domain;
|
|
223
|
+
this.#authClient = new AuthClient({
|
|
224
|
+
domain,
|
|
225
|
+
...this.#authClientOptions,
|
|
226
|
+
telemetry: getTelemetryConfig(this.#options.telemetry)
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async #resolveDomain(storeOptions) {
|
|
231
|
+
if (typeof this.#options.domain === "function") {
|
|
232
|
+
const resolved = await this.#options.domain(storeOptions);
|
|
233
|
+
if (typeof resolved !== "string" || resolved.trim().length === 0) {
|
|
234
|
+
throw new InvalidConfigurationError("domainResolver returned no domain");
|
|
235
|
+
}
|
|
236
|
+
return normalizeDomain(resolved);
|
|
237
|
+
}
|
|
238
|
+
return normalizeDomain(this.#options.domain);
|
|
239
|
+
}
|
|
240
|
+
#createAuthClient(domain) {
|
|
241
|
+
return new AuthClient({
|
|
242
|
+
domain,
|
|
243
|
+
...this.#authClientOptions,
|
|
181
244
|
telemetry: getTelemetryConfig(this.#options.telemetry)
|
|
182
245
|
});
|
|
183
246
|
}
|
|
247
|
+
#getAuthClient(domain) {
|
|
248
|
+
const normalizedDomain = normalizeDomain(domain);
|
|
249
|
+
if (this.#authClient && this.#staticDomain === normalizedDomain) {
|
|
250
|
+
return this.#authClient;
|
|
251
|
+
}
|
|
252
|
+
return this.#createAuthClient(normalizedDomain);
|
|
253
|
+
}
|
|
254
|
+
#getSessionDomain(stateData) {
|
|
255
|
+
if (stateData.domain) {
|
|
256
|
+
return normalizeDomain(stateData.domain);
|
|
257
|
+
}
|
|
258
|
+
if (this.#staticDomain) {
|
|
259
|
+
return this.#staticDomain;
|
|
260
|
+
}
|
|
261
|
+
const issuerFromClaims = stateData.user?.iss;
|
|
262
|
+
if (typeof issuerFromClaims === "string" && issuerFromClaims.trim().length > 0) {
|
|
263
|
+
return normalizeDomain(issuerFromClaims);
|
|
264
|
+
}
|
|
265
|
+
return;
|
|
266
|
+
}
|
|
267
|
+
#isResolverMode() {
|
|
268
|
+
return typeof this.#options.domain === "function";
|
|
269
|
+
}
|
|
270
|
+
async #isSessionForCurrentDomain(stateData, storeOptions) {
|
|
271
|
+
const sessionDomain = this.#getSessionDomain(stateData);
|
|
272
|
+
if (!sessionDomain) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
276
|
+
return sessionDomain === resolvedDomain;
|
|
277
|
+
}
|
|
184
278
|
/**
|
|
185
279
|
* Starts the interactive login process, and returns a URL to redirect the user-agent to to request authorization at Auth0.
|
|
186
280
|
* @param options Optional options used to configure the interactive login process.
|
|
@@ -196,7 +290,9 @@ var ServerClient = class {
|
|
|
196
290
|
throw new MissingRequiredArgumentError("authorizationParams.redirect_uri");
|
|
197
291
|
}
|
|
198
292
|
const scope = ensureOpenIdScope(options?.authorizationParams?.scope ?? this.#options.authorizationParams?.scope);
|
|
199
|
-
const
|
|
293
|
+
const domain = await this.#resolveDomain(storeOptions);
|
|
294
|
+
const authClient = this.#getAuthClient(domain);
|
|
295
|
+
const { codeVerifier, authorizationUrl } = await authClient.buildAuthorizationUrl({
|
|
200
296
|
pushedAuthorizationRequests: options?.pushedAuthorizationRequests,
|
|
201
297
|
authorizationParams: {
|
|
202
298
|
...options?.authorizationParams,
|
|
@@ -206,7 +302,8 @@ var ServerClient = class {
|
|
|
206
302
|
});
|
|
207
303
|
const transactionState = {
|
|
208
304
|
audience: options?.authorizationParams?.audience ?? this.#options.authorizationParams?.audience,
|
|
209
|
-
codeVerifier
|
|
305
|
+
codeVerifier,
|
|
306
|
+
domain
|
|
210
307
|
};
|
|
211
308
|
if (options?.appState) {
|
|
212
309
|
transactionState.appState = options.appState;
|
|
@@ -230,11 +327,15 @@ var ServerClient = class {
|
|
|
230
327
|
if (!transactionData) {
|
|
231
328
|
throw new MissingTransactionError();
|
|
232
329
|
}
|
|
233
|
-
const
|
|
330
|
+
const domain = transactionData.domain ?? await this.#resolveDomain(storeOptions);
|
|
331
|
+
const authClient = this.#getAuthClient(domain);
|
|
332
|
+
const tokenEndpointResponse = await authClient.getTokenByCode(url, {
|
|
234
333
|
codeVerifier: transactionData.codeVerifier
|
|
235
334
|
});
|
|
236
335
|
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
237
|
-
const stateData = updateStateData(transactionData.audience ?? "default", existingStateData, tokenEndpointResponse
|
|
336
|
+
const stateData = updateStateData(transactionData.audience ?? "default", existingStateData, tokenEndpointResponse, {
|
|
337
|
+
domain
|
|
338
|
+
});
|
|
238
339
|
await this.#stateStore.set(this.#stateStoreIdentifier, stateData, true, storeOptions);
|
|
239
340
|
await this.#transactionStore.delete(this.#transactionStoreIdentifier, storeOptions);
|
|
240
341
|
return { appState: transactionData.appState, authorizationDetails: tokenEndpointResponse.authorizationDetails };
|
|
@@ -256,7 +357,15 @@ var ServerClient = class {
|
|
|
256
357
|
"Unable to start the user linking process without a logged in user. Ensure to login using the SDK before starting the user linking process."
|
|
257
358
|
);
|
|
258
359
|
}
|
|
259
|
-
|
|
360
|
+
if (this.#isResolverMode()) {
|
|
361
|
+
const isCurrentDomain = await this.#isSessionForCurrentDomain(stateData, storeOptions);
|
|
362
|
+
if (!isCurrentDomain) {
|
|
363
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
const domain = this.#getSessionDomain(stateData);
|
|
367
|
+
const authClient = this.#getAuthClient(domain);
|
|
368
|
+
const { linkUserUrl, codeVerifier } = await authClient.buildLinkUserUrl({
|
|
260
369
|
connection: options.connection,
|
|
261
370
|
connectionScope: options.connectionScope,
|
|
262
371
|
idToken: stateData.idToken,
|
|
@@ -264,7 +373,8 @@ var ServerClient = class {
|
|
|
264
373
|
});
|
|
265
374
|
const transactionState = {
|
|
266
375
|
audience: options?.authorizationParams?.audience ?? this.#options.authorizationParams?.audience,
|
|
267
|
-
codeVerifier
|
|
376
|
+
codeVerifier,
|
|
377
|
+
domain
|
|
268
378
|
};
|
|
269
379
|
if (options?.appState) {
|
|
270
380
|
transactionState.appState = options.appState;
|
|
@@ -306,14 +416,23 @@ var ServerClient = class {
|
|
|
306
416
|
"Unable to start the user unlinking process without a logged in user. Ensure to login using the SDK before starting the user unlinking process."
|
|
307
417
|
);
|
|
308
418
|
}
|
|
309
|
-
|
|
419
|
+
if (this.#isResolverMode()) {
|
|
420
|
+
const isCurrentDomain = await this.#isSessionForCurrentDomain(stateData, storeOptions);
|
|
421
|
+
if (!isCurrentDomain) {
|
|
422
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
423
|
+
}
|
|
424
|
+
}
|
|
425
|
+
const domain = this.#getSessionDomain(stateData);
|
|
426
|
+
const authClient = this.#getAuthClient(domain);
|
|
427
|
+
const { unlinkUserUrl, codeVerifier } = await authClient.buildUnlinkUserUrl({
|
|
310
428
|
connection: options.connection,
|
|
311
429
|
idToken: stateData.idToken,
|
|
312
430
|
authorizationParams: options.authorizationParams
|
|
313
431
|
});
|
|
314
432
|
const transactionState = {
|
|
315
433
|
audience: options?.authorizationParams?.audience ?? this.#options.authorizationParams?.audience,
|
|
316
|
-
codeVerifier
|
|
434
|
+
codeVerifier,
|
|
435
|
+
domain
|
|
317
436
|
};
|
|
318
437
|
if (options?.appState) {
|
|
319
438
|
transactionState.appState = options.appState;
|
|
@@ -352,7 +471,9 @@ var ServerClient = class {
|
|
|
352
471
|
*/
|
|
353
472
|
async loginBackchannel(options, storeOptions) {
|
|
354
473
|
const scope = ensureOpenIdScope(options.authorizationParams?.scope ?? this.#options.authorizationParams?.scope);
|
|
355
|
-
const
|
|
474
|
+
const domain = await this.#resolveDomain(storeOptions);
|
|
475
|
+
const authClient = this.#getAuthClient(domain);
|
|
476
|
+
const tokenEndpointResponse = await authClient.backchannelAuthentication({
|
|
356
477
|
bindingMessage: options.bindingMessage,
|
|
357
478
|
loginHint: options.loginHint,
|
|
358
479
|
authorizationParams: {
|
|
@@ -364,7 +485,8 @@ var ServerClient = class {
|
|
|
364
485
|
const stateData = updateStateData(
|
|
365
486
|
this.#options.authorizationParams?.audience ?? "default",
|
|
366
487
|
existingStateData,
|
|
367
|
-
tokenEndpointResponse
|
|
488
|
+
tokenEndpointResponse,
|
|
489
|
+
{ domain }
|
|
368
490
|
);
|
|
369
491
|
await this.#stateStore.set(this.#stateStoreIdentifier, stateData, true, storeOptions);
|
|
370
492
|
return {
|
|
@@ -378,16 +500,31 @@ var ServerClient = class {
|
|
|
378
500
|
*/
|
|
379
501
|
async getUser(storeOptions) {
|
|
380
502
|
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
381
|
-
|
|
503
|
+
if (!stateData) {
|
|
504
|
+
return;
|
|
505
|
+
}
|
|
506
|
+
if (this.#isResolverMode()) {
|
|
507
|
+
const isCurrentDomain = await this.#isSessionForCurrentDomain(stateData, storeOptions);
|
|
508
|
+
if (!isCurrentDomain) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
return stateData.user;
|
|
382
513
|
}
|
|
383
514
|
/**
|
|
384
515
|
* Retrieve the user session from the store, or undefined if no session found.
|
|
385
516
|
* @param storeOptions Optional options used to pass to the Transaction and State Store.
|
|
386
|
-
* @returns The
|
|
517
|
+
* @returns The session or undefined if no session found in the store.
|
|
387
518
|
*/
|
|
388
519
|
async getSession(storeOptions) {
|
|
389
520
|
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
390
521
|
if (stateData) {
|
|
522
|
+
if (this.#isResolverMode()) {
|
|
523
|
+
const isCurrentDomain = await this.#isSessionForCurrentDomain(stateData, storeOptions);
|
|
524
|
+
if (!isCurrentDomain) {
|
|
525
|
+
return;
|
|
526
|
+
}
|
|
527
|
+
}
|
|
391
528
|
const { internal, ...sessionData } = stateData;
|
|
392
529
|
return sessionData;
|
|
393
530
|
}
|
|
@@ -395,24 +532,29 @@ var ServerClient = class {
|
|
|
395
532
|
/**
|
|
396
533
|
* 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.
|
|
397
534
|
* Also updates the store when a new token was retrieved from Auth0.
|
|
398
|
-
*
|
|
399
|
-
* @param options Optional options for requesting specific audience/scope.
|
|
400
535
|
* @param storeOptions Optional options used to pass to the Transaction and State Store.
|
|
401
536
|
*
|
|
402
537
|
* @throws {TokenByRefreshTokenError} If the refresh token was not found or there was an issue requesting the access token.
|
|
403
538
|
*
|
|
404
539
|
* @returns The Token Set, containing the access token, as well as additional information.
|
|
405
540
|
*/
|
|
406
|
-
async getAccessToken(
|
|
407
|
-
const
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
)
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
541
|
+
async getAccessToken(storeOptions) {
|
|
542
|
+
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
543
|
+
const audience = this.#options.authorizationParams?.audience ?? "default";
|
|
544
|
+
const scope = this.#options.authorizationParams?.scope;
|
|
545
|
+
const sessionDomain = stateData ? this.#getSessionDomain(stateData) : this.#staticDomain;
|
|
546
|
+
if (this.#isResolverMode()) {
|
|
547
|
+
if (!stateData) {
|
|
548
|
+
throw new MissingSessionError("Unable to retrieve access token without a logged in user.");
|
|
549
|
+
}
|
|
550
|
+
if (!sessionDomain) {
|
|
551
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
552
|
+
}
|
|
553
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
554
|
+
if (sessionDomain !== resolvedDomain) {
|
|
555
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
556
|
+
}
|
|
557
|
+
}
|
|
416
558
|
const tokenSet = stateData?.tokenSets.find(
|
|
417
559
|
(tokenSet2) => tokenSet2.audience === audience && (!scope || compareScopes(tokenSet2.scope, scope))
|
|
418
560
|
);
|
|
@@ -424,14 +566,15 @@ var ServerClient = class {
|
|
|
424
566
|
"The access token has expired and a refresh token was not provided. The user needs to re-authenticate."
|
|
425
567
|
);
|
|
426
568
|
}
|
|
427
|
-
const
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
};
|
|
431
|
-
const
|
|
432
|
-
const
|
|
433
|
-
|
|
434
|
-
|
|
569
|
+
const domainForSession = sessionDomain;
|
|
570
|
+
const tokenEndpointResponse = await this.#getAuthClient(domainForSession).getTokenByRefreshToken({
|
|
571
|
+
refreshToken: stateData.refreshToken
|
|
572
|
+
});
|
|
573
|
+
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
574
|
+
const updatedStateData = updateStateData(audience, existingStateData, tokenEndpointResponse, {
|
|
575
|
+
domain: domainForSession
|
|
576
|
+
});
|
|
577
|
+
await this.#stateStore.set(this.#stateStoreIdentifier, updatedStateData, false, storeOptions);
|
|
435
578
|
return {
|
|
436
579
|
accessToken: tokenEndpointResponse.accessToken,
|
|
437
580
|
scope: tokenEndpointResponse.scope,
|
|
@@ -456,6 +599,19 @@ var ServerClient = class {
|
|
|
456
599
|
*/
|
|
457
600
|
async getAccessTokenForConnection(options, storeOptions) {
|
|
458
601
|
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
602
|
+
const sessionDomain = stateData ? this.#getSessionDomain(stateData) : this.#staticDomain;
|
|
603
|
+
if (this.#isResolverMode()) {
|
|
604
|
+
if (!stateData) {
|
|
605
|
+
throw new MissingSessionError("Unable to retrieve an access token for a connection without a logged in user.");
|
|
606
|
+
}
|
|
607
|
+
if (!sessionDomain) {
|
|
608
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
609
|
+
}
|
|
610
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
611
|
+
if (sessionDomain !== resolvedDomain) {
|
|
612
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
613
|
+
}
|
|
614
|
+
}
|
|
459
615
|
const connectionTokenSet = stateData?.connectionTokenSets?.find(
|
|
460
616
|
(tokenSet) => tokenSet.connection === options.connection
|
|
461
617
|
);
|
|
@@ -467,12 +623,20 @@ var ServerClient = class {
|
|
|
467
623
|
"A refresh token was not found but is required to be able to retrieve an access token for a connection."
|
|
468
624
|
);
|
|
469
625
|
}
|
|
470
|
-
const
|
|
626
|
+
const domainForSession = sessionDomain;
|
|
627
|
+
const tokenEndpointResponse = await this.#getAuthClient(domainForSession).getTokenForConnection({
|
|
471
628
|
connection: options.connection,
|
|
472
629
|
loginHint: options.loginHint,
|
|
473
630
|
refreshToken: stateData.refreshToken
|
|
474
631
|
});
|
|
475
|
-
const updatedStateData = updateStateDataForConnectionTokenSet(
|
|
632
|
+
const updatedStateData = updateStateDataForConnectionTokenSet(
|
|
633
|
+
options,
|
|
634
|
+
{
|
|
635
|
+
...stateData,
|
|
636
|
+
domain: stateData.domain ?? domainForSession
|
|
637
|
+
},
|
|
638
|
+
tokenEndpointResponse
|
|
639
|
+
);
|
|
476
640
|
await this.#stateStore.set(this.#stateStoreIdentifier, updatedStateData, false, storeOptions);
|
|
477
641
|
return {
|
|
478
642
|
accessToken: tokenEndpointResponse.accessToken,
|
|
@@ -489,8 +653,21 @@ var ServerClient = class {
|
|
|
489
653
|
* @returns {URL}
|
|
490
654
|
*/
|
|
491
655
|
async logout(options, storeOptions) {
|
|
492
|
-
|
|
493
|
-
|
|
656
|
+
if (!this.#isResolverMode()) {
|
|
657
|
+
await this.#stateStore.delete(this.#stateStoreIdentifier, storeOptions);
|
|
658
|
+
return this.authClient.buildLogoutUrl(options);
|
|
659
|
+
}
|
|
660
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
661
|
+
const authClient = this.#getAuthClient(resolvedDomain);
|
|
662
|
+
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
663
|
+
const sessionDomain = stateData ? this.#getSessionDomain(stateData) : void 0;
|
|
664
|
+
if (!stateData) {
|
|
665
|
+
return authClient.buildLogoutUrl(options);
|
|
666
|
+
}
|
|
667
|
+
if (sessionDomain && sessionDomain === resolvedDomain) {
|
|
668
|
+
await this.#stateStore.delete(this.#stateStoreIdentifier, storeOptions);
|
|
669
|
+
}
|
|
670
|
+
return authClient.buildLogoutUrl(options);
|
|
494
671
|
}
|
|
495
672
|
/**
|
|
496
673
|
* Handles the backchannel logout process by verifying the logout token and deleting the session from the store if the logout token was considered valid.
|
|
@@ -504,8 +681,23 @@ var ServerClient = class {
|
|
|
504
681
|
if (!logoutToken) {
|
|
505
682
|
throw new BackchannelLogoutError("Missing Logout Token");
|
|
506
683
|
}
|
|
507
|
-
|
|
508
|
-
|
|
684
|
+
if (!this.#isResolverMode()) {
|
|
685
|
+
const logoutTokenClaims2 = await this.authClient.verifyLogoutToken({ logoutToken });
|
|
686
|
+
await this.#stateStore.deleteByLogoutToken(logoutTokenClaims2, storeOptions);
|
|
687
|
+
return;
|
|
688
|
+
}
|
|
689
|
+
const issuer = decodeIssuer(logoutToken);
|
|
690
|
+
if (!issuer) {
|
|
691
|
+
throw new BackchannelLogoutError("Logout token is missing an issuer");
|
|
692
|
+
}
|
|
693
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
694
|
+
const domain = normalizeDomain(issuer);
|
|
695
|
+
if (domain !== resolvedDomain) {
|
|
696
|
+
throw new BackchannelLogoutError("Logout token issuer does not match the resolved domain");
|
|
697
|
+
}
|
|
698
|
+
const authClient = this.#getAuthClient(domain);
|
|
699
|
+
const logoutTokenClaims = await authClient.verifyLogoutToken({ logoutToken });
|
|
700
|
+
await this.#stateStore.deleteByLogoutToken({ ...logoutTokenClaims, iss: issuer }, storeOptions);
|
|
509
701
|
}
|
|
510
702
|
};
|
|
511
703
|
|
|
@@ -829,6 +1021,7 @@ export {
|
|
|
829
1021
|
BackchannelLogoutError,
|
|
830
1022
|
CookieTransactionStore,
|
|
831
1023
|
InvalidConfigurationError,
|
|
1024
|
+
IssuerValidationError,
|
|
832
1025
|
MissingRequiredArgumentError,
|
|
833
1026
|
MissingSessionError,
|
|
834
1027
|
MissingTransactionError,
|