@auth0/auth0-server-js 1.3.0 → 1.5.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 +384 -58
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +143 -8
- package/dist/index.d.ts +143 -8
- package/dist/index.js +378 -53
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -25,13 +25,20 @@ __export(index_exports, {
|
|
|
25
25
|
BackchannelLogoutError: () => BackchannelLogoutError,
|
|
26
26
|
CookieTransactionStore: () => CookieTransactionStore,
|
|
27
27
|
InvalidConfigurationError: () => InvalidConfigurationError,
|
|
28
|
+
IssuerValidationError: () => IssuerValidationError,
|
|
29
|
+
MfaChallengeError: () => import_auth0_auth_js2.MfaChallengeError,
|
|
30
|
+
MfaEnrollmentError: () => import_auth0_auth_js2.MfaEnrollmentError,
|
|
31
|
+
MfaListAuthenticatorsError: () => import_auth0_auth_js2.MfaListAuthenticatorsError,
|
|
32
|
+
MfaVerifyError: () => import_auth0_auth_js2.MfaVerifyError,
|
|
28
33
|
MissingRequiredArgumentError: () => MissingRequiredArgumentError,
|
|
29
34
|
MissingSessionError: () => MissingSessionError,
|
|
30
35
|
MissingTransactionError: () => MissingTransactionError,
|
|
31
36
|
ServerClient: () => ServerClient,
|
|
37
|
+
ServerMfaClient: () => ServerMfaClient,
|
|
32
38
|
StartLinkUserError: () => StartLinkUserError,
|
|
33
39
|
StatefulStateStore: () => StatefulStateStore,
|
|
34
|
-
StatelessStateStore: () => StatelessStateStore
|
|
40
|
+
StatelessStateStore: () => StatelessStateStore,
|
|
41
|
+
isMfaRequiredError: () => import_auth0_auth_js2.isMfaRequiredError
|
|
35
42
|
});
|
|
36
43
|
module.exports = __toCommonJS(index_exports);
|
|
37
44
|
|
|
@@ -78,6 +85,13 @@ var InvalidConfigurationError = class extends Error {
|
|
|
78
85
|
this.name = "InvalidConfigurationError";
|
|
79
86
|
}
|
|
80
87
|
};
|
|
88
|
+
var IssuerValidationError = class extends Error {
|
|
89
|
+
code = "issuer_validation_error";
|
|
90
|
+
constructor(message) {
|
|
91
|
+
super(message);
|
|
92
|
+
this.name = "IssuerValidationError";
|
|
93
|
+
}
|
|
94
|
+
};
|
|
81
95
|
|
|
82
96
|
// src/state/utils.ts
|
|
83
97
|
var createUpdatedTokenSet = (audience, response) => ({
|
|
@@ -86,7 +100,18 @@ var createUpdatedTokenSet = (audience, response) => ({
|
|
|
86
100
|
scope: response.scope,
|
|
87
101
|
expiresAt: response.expiresAt
|
|
88
102
|
});
|
|
89
|
-
function updateStateData(audience, stateData, tokenEndpointResponse) {
|
|
103
|
+
function updateStateData(audience, stateData, tokenEndpointResponse, context) {
|
|
104
|
+
if (stateData && tokenEndpointResponse.claims) {
|
|
105
|
+
const newSub = tokenEndpointResponse.claims.sub;
|
|
106
|
+
const newIss = tokenEndpointResponse.claims.iss;
|
|
107
|
+
const existingSub = stateData.user?.sub;
|
|
108
|
+
const existingIss = stateData.user?.iss;
|
|
109
|
+
const subMismatch = newSub !== void 0 && existingSub !== void 0 && newSub !== existingSub;
|
|
110
|
+
const issMismatch = newIss !== void 0 && existingIss !== void 0 && newIss !== existingIss;
|
|
111
|
+
if (subMismatch || issMismatch) {
|
|
112
|
+
stateData = void 0;
|
|
113
|
+
}
|
|
114
|
+
}
|
|
90
115
|
if (stateData) {
|
|
91
116
|
const isNewTokenSet = !stateData.tokenSets.some(
|
|
92
117
|
(tokenSet) => tokenSet.audience === audience && tokenSet.scope === tokenEndpointResponse.scope
|
|
@@ -98,7 +123,8 @@ function updateStateData(audience, stateData, tokenEndpointResponse) {
|
|
|
98
123
|
...stateData,
|
|
99
124
|
idToken: tokenEndpointResponse.idToken ?? stateData.idToken,
|
|
100
125
|
refreshToken: tokenEndpointResponse.refreshToken ?? stateData.refreshToken,
|
|
101
|
-
tokenSets
|
|
126
|
+
tokenSets,
|
|
127
|
+
domain: context?.domain ?? stateData.domain
|
|
102
128
|
};
|
|
103
129
|
} else {
|
|
104
130
|
const user = tokenEndpointResponse.claims;
|
|
@@ -106,9 +132,8 @@ function updateStateData(audience, stateData, tokenEndpointResponse) {
|
|
|
106
132
|
user,
|
|
107
133
|
idToken: tokenEndpointResponse.idToken,
|
|
108
134
|
refreshToken: tokenEndpointResponse.refreshToken,
|
|
109
|
-
tokenSets: [
|
|
110
|
-
|
|
111
|
-
],
|
|
135
|
+
tokenSets: [createUpdatedTokenSet(audience, tokenEndpointResponse)],
|
|
136
|
+
domain: context?.domain,
|
|
112
137
|
internal: {
|
|
113
138
|
sid: user?.sid,
|
|
114
139
|
createdAt: Math.floor(Date.now() / 1e3)
|
|
@@ -153,6 +178,9 @@ var compareScopes = (scopes, requiredScopes) => {
|
|
|
153
178
|
return requiredScopesArray.every((scope) => scopesSet.has(scope));
|
|
154
179
|
};
|
|
155
180
|
|
|
181
|
+
// src/server-client.ts
|
|
182
|
+
var import_jose = require("jose");
|
|
183
|
+
|
|
156
184
|
// src/telemetry.ts
|
|
157
185
|
function getTelemetryConfig(config) {
|
|
158
186
|
if (config?.enabled === false) {
|
|
@@ -161,13 +189,106 @@ function getTelemetryConfig(config) {
|
|
|
161
189
|
return {
|
|
162
190
|
enabled: true,
|
|
163
191
|
name: config?.name ?? "@auth0/auth0-server-js",
|
|
164
|
-
version: config?.version ?? "1.
|
|
192
|
+
version: config?.version ?? "1.5.0"
|
|
165
193
|
};
|
|
166
194
|
}
|
|
167
195
|
|
|
196
|
+
// src/mfa/server-mfa-client.ts
|
|
197
|
+
var ServerMfaClient = class {
|
|
198
|
+
#options;
|
|
199
|
+
/**
|
|
200
|
+
* @internal
|
|
201
|
+
*/
|
|
202
|
+
constructor(options) {
|
|
203
|
+
this.#options = options;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Lists all MFA authenticators enrolled by the user.
|
|
207
|
+
*
|
|
208
|
+
* @param options - Options for listing authenticators
|
|
209
|
+
* @returns Promise resolving to an array of enrolled authenticators
|
|
210
|
+
* @throws {MfaListAuthenticatorsError} When the request fails
|
|
211
|
+
*/
|
|
212
|
+
async listAuthenticators(options) {
|
|
213
|
+
return this.#options.authClient.mfa.listAuthenticators(options);
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Enrolls a new MFA authenticator for the user.
|
|
217
|
+
*
|
|
218
|
+
* @param options - Enrollment options
|
|
219
|
+
* @returns Promise resolving to enrollment response with authenticator details
|
|
220
|
+
* @throws {MfaEnrollmentError} When enrollment fails
|
|
221
|
+
*/
|
|
222
|
+
async enrollAuthenticator(options) {
|
|
223
|
+
return this.#options.authClient.mfa.enrollAuthenticator(options);
|
|
224
|
+
}
|
|
225
|
+
/**
|
|
226
|
+
* Initiates an MFA challenge for user verification.
|
|
227
|
+
*
|
|
228
|
+
* @param options - Challenge options
|
|
229
|
+
* @returns Promise resolving to challenge response with challenge details
|
|
230
|
+
* @throws {MfaChallengeError} When the challenge fails
|
|
231
|
+
*/
|
|
232
|
+
async challengeAuthenticator(options) {
|
|
233
|
+
return this.#options.authClient.mfa.challengeAuthenticator(options);
|
|
234
|
+
}
|
|
235
|
+
/**
|
|
236
|
+
* Verifies an MFA challenge and completes the authentication flow.
|
|
237
|
+
*
|
|
238
|
+
* Exchanges the MFA token and verification code for access, ID, and refresh tokens,
|
|
239
|
+
* then saves them into the user's session automatically.
|
|
240
|
+
*
|
|
241
|
+
* @param options - The MFA token, factor type (otp / oob / recovery-code), and the code to verify
|
|
242
|
+
* @param storeOptions - Optional options forwarded to the session store. Can be omitted when
|
|
243
|
+
* using the built-in stores; required if your custom store needs extra context (e.g. a request object).
|
|
244
|
+
* @returns The tokens returned by Auth0 after successful verification
|
|
245
|
+
* @throws {MfaVerifyError} When verification fails (e.g. invalid token, wrong code)
|
|
246
|
+
*/
|
|
247
|
+
async verify(options, storeOptions) {
|
|
248
|
+
const tokenResponse = await this.#options.authClient.mfa.verify(options);
|
|
249
|
+
const audience = options.audience ?? this.#options.defaultAudience;
|
|
250
|
+
const existingStateData = await this.#options.stateStore.get(
|
|
251
|
+
this.#options.stateStoreIdentifier,
|
|
252
|
+
storeOptions
|
|
253
|
+
);
|
|
254
|
+
const updatedStateData = updateStateData(audience, existingStateData, tokenResponse, {
|
|
255
|
+
domain: this.#options.domain
|
|
256
|
+
});
|
|
257
|
+
await this.#options.stateStore.set(
|
|
258
|
+
this.#options.stateStoreIdentifier,
|
|
259
|
+
updatedStateData,
|
|
260
|
+
true,
|
|
261
|
+
storeOptions
|
|
262
|
+
);
|
|
263
|
+
const result = {
|
|
264
|
+
accessToken: tokenResponse.accessToken,
|
|
265
|
+
tokenType: tokenResponse.tokenType ?? "bearer",
|
|
266
|
+
expiresAt: tokenResponse.expiresAt,
|
|
267
|
+
scope: tokenResponse.scope
|
|
268
|
+
};
|
|
269
|
+
if (tokenResponse.idToken) result.idToken = tokenResponse.idToken;
|
|
270
|
+
if (tokenResponse.refreshToken) result.refreshToken = tokenResponse.refreshToken;
|
|
271
|
+
if (tokenResponse.recoveryCode) result.recoveryCode = tokenResponse.recoveryCode;
|
|
272
|
+
return result;
|
|
273
|
+
}
|
|
274
|
+
};
|
|
275
|
+
|
|
168
276
|
// src/server-client.ts
|
|
169
277
|
var DEFAULT_SCOPES = "openid profile email offline_access";
|
|
170
|
-
|
|
278
|
+
var normalizeDomain = (value) => {
|
|
279
|
+
const trimmed = value.trim();
|
|
280
|
+
const parsed = trimmed.startsWith("http") ? new URL(trimmed) : new URL(`https://${trimmed}`);
|
|
281
|
+
return parsed.host.toLowerCase();
|
|
282
|
+
};
|
|
283
|
+
var decodeIssuer = (token) => {
|
|
284
|
+
try {
|
|
285
|
+
const { iss } = (0, import_jose.decodeJwt)(token);
|
|
286
|
+
return typeof iss === "string" ? iss : void 0;
|
|
287
|
+
} catch {
|
|
288
|
+
return void 0;
|
|
289
|
+
}
|
|
290
|
+
};
|
|
291
|
+
var ensureOpenIdScope = (scope) => {
|
|
171
292
|
if (!scope) {
|
|
172
293
|
return DEFAULT_SCOPES;
|
|
173
294
|
}
|
|
@@ -176,20 +297,51 @@ function ensureOpenIdScope(scope) {
|
|
|
176
297
|
scopes.unshift("openid");
|
|
177
298
|
}
|
|
178
299
|
return scopes.join(" ");
|
|
179
|
-
}
|
|
300
|
+
};
|
|
180
301
|
var ServerClient = class {
|
|
181
302
|
#options;
|
|
182
303
|
#transactionStore;
|
|
183
304
|
#transactionStoreIdentifier;
|
|
184
305
|
#stateStore;
|
|
185
306
|
#stateStoreIdentifier;
|
|
307
|
+
#authClientOptions;
|
|
308
|
+
#staticDomain;
|
|
309
|
+
#authClient;
|
|
310
|
+
#mfaClient;
|
|
186
311
|
/**
|
|
187
312
|
* The underlying `authClient` instance that can be used to interact with the Auth0 Authentication API.
|
|
188
313
|
* Generally, you should prefer to use the higher-level methods exposed on the `ServerClient` instance.
|
|
189
|
-
*
|
|
314
|
+
*
|
|
315
|
+
* This property can only be used when `domain` is configured as a static string.
|
|
316
|
+
* In resolver mode (`domain` as a function), the SDK resolves the domain per request,
|
|
317
|
+
* so use `ServerClient` methods instead.
|
|
318
|
+
*
|
|
190
319
|
* Important: the methods exposed on the `authClient` instance do not handle any session or state management.
|
|
191
320
|
*/
|
|
192
|
-
authClient
|
|
321
|
+
get authClient() {
|
|
322
|
+
if (!this.#authClient) {
|
|
323
|
+
throw new InvalidConfigurationError("authClient is only available when using a static domain configuration.");
|
|
324
|
+
}
|
|
325
|
+
return this.#authClient;
|
|
326
|
+
}
|
|
327
|
+
/**
|
|
328
|
+
* The MFA client for managing multi-factor authentication operations.
|
|
329
|
+
*
|
|
330
|
+
* Provides methods to list, enroll, and challenge MFA authenticators,
|
|
331
|
+
* as well as verify MFA challenges to complete authentication.
|
|
332
|
+
*
|
|
333
|
+
* The `verify` method integrates with the session state store, persisting tokens
|
|
334
|
+
* and user data after successful MFA verification.
|
|
335
|
+
*
|
|
336
|
+
* This property can only be used when `domain` is configured as a static string.
|
|
337
|
+
* In resolver mode (`domain` as a function), MFA is not supported.
|
|
338
|
+
*/
|
|
339
|
+
get mfa() {
|
|
340
|
+
if (!this.#mfaClient) {
|
|
341
|
+
throw new InvalidConfigurationError("mfa is only available when using a static domain configuration.");
|
|
342
|
+
}
|
|
343
|
+
return this.#mfaClient;
|
|
344
|
+
}
|
|
193
345
|
constructor(options) {
|
|
194
346
|
this.#options = options;
|
|
195
347
|
this.#stateStoreIdentifier = this.#options.stateIdentifier || "__a0_session";
|
|
@@ -202,18 +354,84 @@ var ServerClient = class {
|
|
|
202
354
|
if (!this.#options.transactionStore) {
|
|
203
355
|
throw new MissingRequiredArgumentError("transactionStore");
|
|
204
356
|
}
|
|
205
|
-
this.
|
|
206
|
-
domain
|
|
357
|
+
if (typeof this.#options.domain !== "string" && typeof this.#options.domain !== "function") {
|
|
358
|
+
throw new InvalidConfigurationError("domain must be a string or resolver function");
|
|
359
|
+
}
|
|
360
|
+
this.#authClientOptions = {
|
|
207
361
|
clientId: this.#options.clientId,
|
|
208
362
|
clientSecret: this.#options.clientSecret,
|
|
209
363
|
clientAssertionSigningKey: this.#options.clientAssertionSigningKey,
|
|
210
364
|
clientAssertionSigningAlg: this.#options.clientAssertionSigningAlg,
|
|
211
365
|
authorizationParams: this.#options.authorizationParams,
|
|
366
|
+
discoveryCache: this.#options.discoveryCache,
|
|
212
367
|
customFetch: this.#options.customFetch,
|
|
213
|
-
useMtls: this.#options.useMtls
|
|
368
|
+
useMtls: this.#options.useMtls
|
|
369
|
+
};
|
|
370
|
+
if (typeof this.#options.domain === "string") {
|
|
371
|
+
const domain = normalizeDomain(this.#options.domain);
|
|
372
|
+
this.#staticDomain = domain;
|
|
373
|
+
this.#authClient = new import_auth0_auth_js.AuthClient({
|
|
374
|
+
domain,
|
|
375
|
+
...this.#authClientOptions,
|
|
376
|
+
telemetry: getTelemetryConfig(this.#options.telemetry)
|
|
377
|
+
});
|
|
378
|
+
this.#mfaClient = new ServerMfaClient({
|
|
379
|
+
authClient: this.#authClient,
|
|
380
|
+
domain,
|
|
381
|
+
stateStore: this.#stateStore,
|
|
382
|
+
stateStoreIdentifier: this.#stateStoreIdentifier,
|
|
383
|
+
defaultAudience: this.#options.authorizationParams?.audience ?? "default"
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
async #resolveDomain(storeOptions) {
|
|
388
|
+
if (typeof this.#options.domain === "function") {
|
|
389
|
+
const resolved = await this.#options.domain(storeOptions);
|
|
390
|
+
if (typeof resolved !== "string" || resolved.trim().length === 0) {
|
|
391
|
+
throw new InvalidConfigurationError("domainResolver returned no domain");
|
|
392
|
+
}
|
|
393
|
+
return normalizeDomain(resolved);
|
|
394
|
+
}
|
|
395
|
+
return normalizeDomain(this.#options.domain);
|
|
396
|
+
}
|
|
397
|
+
#createAuthClient(domain) {
|
|
398
|
+
return new import_auth0_auth_js.AuthClient({
|
|
399
|
+
domain,
|
|
400
|
+
...this.#authClientOptions,
|
|
214
401
|
telemetry: getTelemetryConfig(this.#options.telemetry)
|
|
215
402
|
});
|
|
216
403
|
}
|
|
404
|
+
#getAuthClient(domain) {
|
|
405
|
+
const normalizedDomain = normalizeDomain(domain);
|
|
406
|
+
if (this.#authClient && this.#staticDomain === normalizedDomain) {
|
|
407
|
+
return this.#authClient;
|
|
408
|
+
}
|
|
409
|
+
return this.#createAuthClient(normalizedDomain);
|
|
410
|
+
}
|
|
411
|
+
#getSessionDomain(stateData) {
|
|
412
|
+
if (stateData.domain) {
|
|
413
|
+
return normalizeDomain(stateData.domain);
|
|
414
|
+
}
|
|
415
|
+
if (this.#staticDomain) {
|
|
416
|
+
return this.#staticDomain;
|
|
417
|
+
}
|
|
418
|
+
const issuerFromClaims = stateData.user?.iss;
|
|
419
|
+
if (typeof issuerFromClaims === "string" && issuerFromClaims.trim().length > 0) {
|
|
420
|
+
return normalizeDomain(issuerFromClaims);
|
|
421
|
+
}
|
|
422
|
+
return;
|
|
423
|
+
}
|
|
424
|
+
#isResolverMode() {
|
|
425
|
+
return typeof this.#options.domain === "function";
|
|
426
|
+
}
|
|
427
|
+
async #isSessionForCurrentDomain(stateData, storeOptions) {
|
|
428
|
+
const sessionDomain = this.#getSessionDomain(stateData);
|
|
429
|
+
if (!sessionDomain) {
|
|
430
|
+
return false;
|
|
431
|
+
}
|
|
432
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
433
|
+
return sessionDomain === resolvedDomain;
|
|
434
|
+
}
|
|
217
435
|
/**
|
|
218
436
|
* Starts the interactive login process, and returns a URL to redirect the user-agent to to request authorization at Auth0.
|
|
219
437
|
* @param options Optional options used to configure the interactive login process.
|
|
@@ -229,7 +447,9 @@ var ServerClient = class {
|
|
|
229
447
|
throw new MissingRequiredArgumentError("authorizationParams.redirect_uri");
|
|
230
448
|
}
|
|
231
449
|
const scope = ensureOpenIdScope(options?.authorizationParams?.scope ?? this.#options.authorizationParams?.scope);
|
|
232
|
-
const
|
|
450
|
+
const domain = await this.#resolveDomain(storeOptions);
|
|
451
|
+
const authClient = this.#getAuthClient(domain);
|
|
452
|
+
const { codeVerifier, authorizationUrl } = await authClient.buildAuthorizationUrl({
|
|
233
453
|
pushedAuthorizationRequests: options?.pushedAuthorizationRequests,
|
|
234
454
|
authorizationParams: {
|
|
235
455
|
...options?.authorizationParams,
|
|
@@ -239,7 +459,8 @@ var ServerClient = class {
|
|
|
239
459
|
});
|
|
240
460
|
const transactionState = {
|
|
241
461
|
audience: options?.authorizationParams?.audience ?? this.#options.authorizationParams?.audience,
|
|
242
|
-
codeVerifier
|
|
462
|
+
codeVerifier,
|
|
463
|
+
domain
|
|
243
464
|
};
|
|
244
465
|
if (options?.appState) {
|
|
245
466
|
transactionState.appState = options.appState;
|
|
@@ -263,11 +484,15 @@ var ServerClient = class {
|
|
|
263
484
|
if (!transactionData) {
|
|
264
485
|
throw new MissingTransactionError();
|
|
265
486
|
}
|
|
266
|
-
const
|
|
487
|
+
const domain = transactionData.domain ?? await this.#resolveDomain(storeOptions);
|
|
488
|
+
const authClient = this.#getAuthClient(domain);
|
|
489
|
+
const tokenEndpointResponse = await authClient.getTokenByCode(url, {
|
|
267
490
|
codeVerifier: transactionData.codeVerifier
|
|
268
491
|
});
|
|
269
492
|
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
270
|
-
const stateData = updateStateData(transactionData.audience ?? "default", existingStateData, tokenEndpointResponse
|
|
493
|
+
const stateData = updateStateData(transactionData.audience ?? "default", existingStateData, tokenEndpointResponse, {
|
|
494
|
+
domain
|
|
495
|
+
});
|
|
271
496
|
await this.#stateStore.set(this.#stateStoreIdentifier, stateData, true, storeOptions);
|
|
272
497
|
await this.#transactionStore.delete(this.#transactionStoreIdentifier, storeOptions);
|
|
273
498
|
return { appState: transactionData.appState, authorizationDetails: tokenEndpointResponse.authorizationDetails };
|
|
@@ -289,7 +514,15 @@ var ServerClient = class {
|
|
|
289
514
|
"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
515
|
);
|
|
291
516
|
}
|
|
292
|
-
|
|
517
|
+
if (this.#isResolverMode()) {
|
|
518
|
+
const isCurrentDomain = await this.#isSessionForCurrentDomain(stateData, storeOptions);
|
|
519
|
+
if (!isCurrentDomain) {
|
|
520
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
const domain = this.#getSessionDomain(stateData);
|
|
524
|
+
const authClient = this.#getAuthClient(domain);
|
|
525
|
+
const { linkUserUrl, codeVerifier } = await authClient.buildLinkUserUrl({
|
|
293
526
|
connection: options.connection,
|
|
294
527
|
connectionScope: options.connectionScope,
|
|
295
528
|
idToken: stateData.idToken,
|
|
@@ -297,7 +530,8 @@ var ServerClient = class {
|
|
|
297
530
|
});
|
|
298
531
|
const transactionState = {
|
|
299
532
|
audience: options?.authorizationParams?.audience ?? this.#options.authorizationParams?.audience,
|
|
300
|
-
codeVerifier
|
|
533
|
+
codeVerifier,
|
|
534
|
+
domain
|
|
301
535
|
};
|
|
302
536
|
if (options?.appState) {
|
|
303
537
|
transactionState.appState = options.appState;
|
|
@@ -339,14 +573,23 @@ var ServerClient = class {
|
|
|
339
573
|
"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
574
|
);
|
|
341
575
|
}
|
|
342
|
-
|
|
576
|
+
if (this.#isResolverMode()) {
|
|
577
|
+
const isCurrentDomain = await this.#isSessionForCurrentDomain(stateData, storeOptions);
|
|
578
|
+
if (!isCurrentDomain) {
|
|
579
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
580
|
+
}
|
|
581
|
+
}
|
|
582
|
+
const domain = this.#getSessionDomain(stateData);
|
|
583
|
+
const authClient = this.#getAuthClient(domain);
|
|
584
|
+
const { unlinkUserUrl, codeVerifier } = await authClient.buildUnlinkUserUrl({
|
|
343
585
|
connection: options.connection,
|
|
344
586
|
idToken: stateData.idToken,
|
|
345
587
|
authorizationParams: options.authorizationParams
|
|
346
588
|
});
|
|
347
589
|
const transactionState = {
|
|
348
590
|
audience: options?.authorizationParams?.audience ?? this.#options.authorizationParams?.audience,
|
|
349
|
-
codeVerifier
|
|
591
|
+
codeVerifier,
|
|
592
|
+
domain
|
|
350
593
|
};
|
|
351
594
|
if (options?.appState) {
|
|
352
595
|
transactionState.appState = options.appState;
|
|
@@ -385,7 +628,9 @@ var ServerClient = class {
|
|
|
385
628
|
*/
|
|
386
629
|
async loginBackchannel(options, storeOptions) {
|
|
387
630
|
const scope = ensureOpenIdScope(options.authorizationParams?.scope ?? this.#options.authorizationParams?.scope);
|
|
388
|
-
const
|
|
631
|
+
const domain = await this.#resolveDomain(storeOptions);
|
|
632
|
+
const authClient = this.#getAuthClient(domain);
|
|
633
|
+
const tokenEndpointResponse = await authClient.backchannelAuthentication({
|
|
389
634
|
bindingMessage: options.bindingMessage,
|
|
390
635
|
loginHint: options.loginHint,
|
|
391
636
|
authorizationParams: {
|
|
@@ -397,7 +642,8 @@ var ServerClient = class {
|
|
|
397
642
|
const stateData = updateStateData(
|
|
398
643
|
this.#options.authorizationParams?.audience ?? "default",
|
|
399
644
|
existingStateData,
|
|
400
|
-
tokenEndpointResponse
|
|
645
|
+
tokenEndpointResponse,
|
|
646
|
+
{ domain }
|
|
401
647
|
);
|
|
402
648
|
await this.#stateStore.set(this.#stateStoreIdentifier, stateData, true, storeOptions);
|
|
403
649
|
return {
|
|
@@ -411,16 +657,31 @@ var ServerClient = class {
|
|
|
411
657
|
*/
|
|
412
658
|
async getUser(storeOptions) {
|
|
413
659
|
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
414
|
-
|
|
660
|
+
if (!stateData) {
|
|
661
|
+
return;
|
|
662
|
+
}
|
|
663
|
+
if (this.#isResolverMode()) {
|
|
664
|
+
const isCurrentDomain = await this.#isSessionForCurrentDomain(stateData, storeOptions);
|
|
665
|
+
if (!isCurrentDomain) {
|
|
666
|
+
return;
|
|
667
|
+
}
|
|
668
|
+
}
|
|
669
|
+
return stateData.user;
|
|
415
670
|
}
|
|
416
671
|
/**
|
|
417
672
|
* Retrieve the user session from the store, or undefined if no session found.
|
|
418
673
|
* @param storeOptions Optional options used to pass to the Transaction and State Store.
|
|
419
|
-
* @returns The
|
|
674
|
+
* @returns The session or undefined if no session found in the store.
|
|
420
675
|
*/
|
|
421
676
|
async getSession(storeOptions) {
|
|
422
677
|
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
423
678
|
if (stateData) {
|
|
679
|
+
if (this.#isResolverMode()) {
|
|
680
|
+
const isCurrentDomain = await this.#isSessionForCurrentDomain(stateData, storeOptions);
|
|
681
|
+
if (!isCurrentDomain) {
|
|
682
|
+
return;
|
|
683
|
+
}
|
|
684
|
+
}
|
|
424
685
|
const { internal, ...sessionData } = stateData;
|
|
425
686
|
return sessionData;
|
|
426
687
|
}
|
|
@@ -428,24 +689,29 @@ var ServerClient = class {
|
|
|
428
689
|
/**
|
|
429
690
|
* 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
691
|
* Also updates the store when a new token was retrieved from Auth0.
|
|
431
|
-
*
|
|
432
|
-
* @param options Optional options for requesting specific audience/scope.
|
|
433
692
|
* @param storeOptions Optional options used to pass to the Transaction and State Store.
|
|
434
693
|
*
|
|
435
|
-
* @throws {TokenByRefreshTokenError} If the refresh token was not found or there was an issue requesting the access token.
|
|
694
|
+
* @throws {TokenByRefreshTokenError} If the refresh token was not found or there was an issue requesting the access token. When the cause is `mfa_required`, use `isMfaRequiredError(error)` to narrow the error and read `cause.mfa_token`.
|
|
436
695
|
*
|
|
437
696
|
* @returns The Token Set, containing the access token, as well as additional information.
|
|
438
697
|
*/
|
|
439
|
-
async getAccessToken(
|
|
440
|
-
const
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
)
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
698
|
+
async getAccessToken(storeOptions) {
|
|
699
|
+
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
700
|
+
const audience = this.#options.authorizationParams?.audience ?? "default";
|
|
701
|
+
const scope = this.#options.authorizationParams?.scope;
|
|
702
|
+
const sessionDomain = stateData ? this.#getSessionDomain(stateData) : this.#staticDomain;
|
|
703
|
+
if (this.#isResolverMode()) {
|
|
704
|
+
if (!stateData) {
|
|
705
|
+
throw new MissingSessionError("Unable to retrieve access token without a logged in user.");
|
|
706
|
+
}
|
|
707
|
+
if (!sessionDomain) {
|
|
708
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
709
|
+
}
|
|
710
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
711
|
+
if (sessionDomain !== resolvedDomain) {
|
|
712
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
713
|
+
}
|
|
714
|
+
}
|
|
449
715
|
const tokenSet = stateData?.tokenSets.find(
|
|
450
716
|
(tokenSet2) => tokenSet2.audience === audience && (!scope || compareScopes(tokenSet2.scope, scope))
|
|
451
717
|
);
|
|
@@ -457,14 +723,15 @@ var ServerClient = class {
|
|
|
457
723
|
"The access token has expired and a refresh token was not provided. The user needs to re-authenticate."
|
|
458
724
|
);
|
|
459
725
|
}
|
|
460
|
-
const
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
};
|
|
464
|
-
const
|
|
465
|
-
const
|
|
466
|
-
|
|
467
|
-
|
|
726
|
+
const domainForSession = sessionDomain;
|
|
727
|
+
const tokenEndpointResponse = await this.#getAuthClient(domainForSession).getTokenByRefreshToken({
|
|
728
|
+
refreshToken: stateData.refreshToken
|
|
729
|
+
});
|
|
730
|
+
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
731
|
+
const updatedStateData = updateStateData(audience, existingStateData, tokenEndpointResponse, {
|
|
732
|
+
domain: domainForSession
|
|
733
|
+
});
|
|
734
|
+
await this.#stateStore.set(this.#stateStoreIdentifier, updatedStateData, false, storeOptions);
|
|
468
735
|
return {
|
|
469
736
|
accessToken: tokenEndpointResponse.accessToken,
|
|
470
737
|
scope: tokenEndpointResponse.scope,
|
|
@@ -489,6 +756,19 @@ var ServerClient = class {
|
|
|
489
756
|
*/
|
|
490
757
|
async getAccessTokenForConnection(options, storeOptions) {
|
|
491
758
|
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
759
|
+
const sessionDomain = stateData ? this.#getSessionDomain(stateData) : this.#staticDomain;
|
|
760
|
+
if (this.#isResolverMode()) {
|
|
761
|
+
if (!stateData) {
|
|
762
|
+
throw new MissingSessionError("Unable to retrieve an access token for a connection without a logged in user.");
|
|
763
|
+
}
|
|
764
|
+
if (!sessionDomain) {
|
|
765
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
766
|
+
}
|
|
767
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
768
|
+
if (sessionDomain !== resolvedDomain) {
|
|
769
|
+
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
770
|
+
}
|
|
771
|
+
}
|
|
492
772
|
const connectionTokenSet = stateData?.connectionTokenSets?.find(
|
|
493
773
|
(tokenSet) => tokenSet.connection === options.connection
|
|
494
774
|
);
|
|
@@ -500,12 +780,20 @@ var ServerClient = class {
|
|
|
500
780
|
"A refresh token was not found but is required to be able to retrieve an access token for a connection."
|
|
501
781
|
);
|
|
502
782
|
}
|
|
503
|
-
const
|
|
783
|
+
const domainForSession = sessionDomain;
|
|
784
|
+
const tokenEndpointResponse = await this.#getAuthClient(domainForSession).getTokenForConnection({
|
|
504
785
|
connection: options.connection,
|
|
505
786
|
loginHint: options.loginHint,
|
|
506
787
|
refreshToken: stateData.refreshToken
|
|
507
788
|
});
|
|
508
|
-
const updatedStateData = updateStateDataForConnectionTokenSet(
|
|
789
|
+
const updatedStateData = updateStateDataForConnectionTokenSet(
|
|
790
|
+
options,
|
|
791
|
+
{
|
|
792
|
+
...stateData,
|
|
793
|
+
domain: stateData.domain ?? domainForSession
|
|
794
|
+
},
|
|
795
|
+
tokenEndpointResponse
|
|
796
|
+
);
|
|
509
797
|
await this.#stateStore.set(this.#stateStoreIdentifier, updatedStateData, false, storeOptions);
|
|
510
798
|
return {
|
|
511
799
|
accessToken: tokenEndpointResponse.accessToken,
|
|
@@ -522,8 +810,21 @@ var ServerClient = class {
|
|
|
522
810
|
* @returns {URL}
|
|
523
811
|
*/
|
|
524
812
|
async logout(options, storeOptions) {
|
|
525
|
-
|
|
526
|
-
|
|
813
|
+
if (!this.#isResolverMode()) {
|
|
814
|
+
await this.#stateStore.delete(this.#stateStoreIdentifier, storeOptions);
|
|
815
|
+
return this.authClient.buildLogoutUrl(options);
|
|
816
|
+
}
|
|
817
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
818
|
+
const authClient = this.#getAuthClient(resolvedDomain);
|
|
819
|
+
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
820
|
+
const sessionDomain = stateData ? this.#getSessionDomain(stateData) : void 0;
|
|
821
|
+
if (!stateData) {
|
|
822
|
+
return authClient.buildLogoutUrl(options);
|
|
823
|
+
}
|
|
824
|
+
if (sessionDomain && sessionDomain === resolvedDomain) {
|
|
825
|
+
await this.#stateStore.delete(this.#stateStoreIdentifier, storeOptions);
|
|
826
|
+
}
|
|
827
|
+
return authClient.buildLogoutUrl(options);
|
|
527
828
|
}
|
|
528
829
|
/**
|
|
529
830
|
* 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 +838,28 @@ var ServerClient = class {
|
|
|
537
838
|
if (!logoutToken) {
|
|
538
839
|
throw new BackchannelLogoutError("Missing Logout Token");
|
|
539
840
|
}
|
|
540
|
-
|
|
541
|
-
|
|
841
|
+
if (!this.#isResolverMode()) {
|
|
842
|
+
const logoutTokenClaims2 = await this.authClient.verifyLogoutToken({ logoutToken });
|
|
843
|
+
await this.#stateStore.deleteByLogoutToken(logoutTokenClaims2, storeOptions);
|
|
844
|
+
return;
|
|
845
|
+
}
|
|
846
|
+
const issuer = decodeIssuer(logoutToken);
|
|
847
|
+
if (!issuer) {
|
|
848
|
+
throw new BackchannelLogoutError("Logout token is missing an issuer");
|
|
849
|
+
}
|
|
850
|
+
const resolvedDomain = await this.#resolveDomain(storeOptions);
|
|
851
|
+
const domain = normalizeDomain(issuer);
|
|
852
|
+
if (domain !== resolvedDomain) {
|
|
853
|
+
throw new BackchannelLogoutError("Logout token issuer does not match the resolved domain");
|
|
854
|
+
}
|
|
855
|
+
const authClient = this.#getAuthClient(domain);
|
|
856
|
+
const logoutTokenClaims = await authClient.verifyLogoutToken({ logoutToken });
|
|
857
|
+
await this.#stateStore.deleteByLogoutToken({ ...logoutTokenClaims, iss: issuer }, storeOptions);
|
|
542
858
|
}
|
|
543
859
|
};
|
|
544
860
|
|
|
545
861
|
// src/encryption/index.ts
|
|
546
|
-
var
|
|
862
|
+
var import_jose2 = require("jose");
|
|
547
863
|
var ENC = "A256CBC-HS512";
|
|
548
864
|
var ALG = "dir";
|
|
549
865
|
var DIGEST = "SHA-256";
|
|
@@ -551,7 +867,7 @@ var BIT_LENGTH = 512;
|
|
|
551
867
|
var HKDF_INFO = "derived cookie encryption secret";
|
|
552
868
|
var encoder;
|
|
553
869
|
function isDecryptionError(e) {
|
|
554
|
-
return e instanceof
|
|
870
|
+
return e instanceof import_jose2.errors.JWEDecryptionFailed || e instanceof import_jose2.errors.JWEInvalid;
|
|
555
871
|
}
|
|
556
872
|
async function deriveEncryptionSecret(secret, salt, kid) {
|
|
557
873
|
encoder ||= new TextEncoder();
|
|
@@ -572,7 +888,7 @@ async function deriveEncryptionSecret(secret, salt, kid) {
|
|
|
572
888
|
async function _encrypt(payload, secret, salt, expiration) {
|
|
573
889
|
const kid = crypto.randomUUID();
|
|
574
890
|
const encryptionSecret = await deriveEncryptionSecret(secret, salt, kid);
|
|
575
|
-
return await new
|
|
891
|
+
return await new import_jose2.EncryptJWT(payload).setProtectedHeader({ enc: ENC, alg: ALG, kid }).setExpirationTime(expiration).encrypt(encryptionSecret);
|
|
576
892
|
}
|
|
577
893
|
async function encrypt(payload, secret, salt, expiration) {
|
|
578
894
|
if (typeof secret === "string") {
|
|
@@ -586,7 +902,7 @@ async function encrypt(payload, secret, salt, expiration) {
|
|
|
586
902
|
}
|
|
587
903
|
}
|
|
588
904
|
async function _decrypt(value, secret, salt) {
|
|
589
|
-
const res = await (0,
|
|
905
|
+
const res = await (0, import_jose2.jwtDecrypt)(
|
|
590
906
|
value,
|
|
591
907
|
async (protectedHeader) => {
|
|
592
908
|
if (!protectedHeader.kid) {
|
|
@@ -856,6 +1172,9 @@ var StatelessStateStore = class extends AbstractSessionStore {
|
|
|
856
1172
|
};
|
|
857
1173
|
}
|
|
858
1174
|
};
|
|
1175
|
+
|
|
1176
|
+
// src/mfa/index.ts
|
|
1177
|
+
var import_auth0_auth_js2 = require("@auth0/auth0-auth-js");
|
|
859
1178
|
// Annotate the CommonJS export names for ESM import in node:
|
|
860
1179
|
0 && (module.exports = {
|
|
861
1180
|
AbstractStateStore,
|
|
@@ -863,12 +1182,19 @@ var StatelessStateStore = class extends AbstractSessionStore {
|
|
|
863
1182
|
BackchannelLogoutError,
|
|
864
1183
|
CookieTransactionStore,
|
|
865
1184
|
InvalidConfigurationError,
|
|
1185
|
+
IssuerValidationError,
|
|
1186
|
+
MfaChallengeError,
|
|
1187
|
+
MfaEnrollmentError,
|
|
1188
|
+
MfaListAuthenticatorsError,
|
|
1189
|
+
MfaVerifyError,
|
|
866
1190
|
MissingRequiredArgumentError,
|
|
867
1191
|
MissingSessionError,
|
|
868
1192
|
MissingTransactionError,
|
|
869
1193
|
ServerClient,
|
|
1194
|
+
ServerMfaClient,
|
|
870
1195
|
StartLinkUserError,
|
|
871
1196
|
StatefulStateStore,
|
|
872
|
-
StatelessStateStore
|
|
1197
|
+
StatelessStateStore,
|
|
1198
|
+
isMfaRequiredError
|
|
873
1199
|
});
|
|
874
1200
|
//# sourceMappingURL=index.cjs.map
|