@auth0/auth0-server-js 1.6.1 → 1.7.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/dist/index.cjs +346 -25
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +304 -13
- package/dist/index.d.ts +304 -13
- package/dist/index.js +345 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -30,16 +30,21 @@ __export(index_exports, {
|
|
|
30
30
|
MfaEnrollmentError: () => import_auth0_auth_js2.MfaEnrollmentError,
|
|
31
31
|
MfaListAuthenticatorsError: () => import_auth0_auth_js2.MfaListAuthenticatorsError,
|
|
32
32
|
MfaVerifyError: () => import_auth0_auth_js2.MfaVerifyError,
|
|
33
|
-
MissingClientAuthError: () =>
|
|
33
|
+
MissingClientAuthError: () => import_auth0_auth_js4.MissingClientAuthError,
|
|
34
34
|
MissingRequiredArgumentError: () => MissingRequiredArgumentError,
|
|
35
35
|
MissingSessionError: () => MissingSessionError,
|
|
36
36
|
MissingTransactionError: () => MissingTransactionError,
|
|
37
|
+
OrganizationValidationError: () => import_auth0_auth_js3.OrganizationValidationError,
|
|
38
|
+
PasskeyChallengeError: () => import_auth0_auth_js3.PasskeyChallengeError,
|
|
39
|
+
PasskeyGetTokenError: () => import_auth0_auth_js3.PasskeyGetTokenError,
|
|
40
|
+
PasskeyRegisterError: () => import_auth0_auth_js3.PasskeyRegisterError,
|
|
37
41
|
ServerClient: () => ServerClient,
|
|
38
42
|
ServerMfaClient: () => ServerMfaClient,
|
|
43
|
+
ServerPasskeyClient: () => ServerPasskeyClient,
|
|
39
44
|
StartLinkUserError: () => StartLinkUserError,
|
|
40
45
|
StatefulStateStore: () => StatefulStateStore,
|
|
41
46
|
StatelessStateStore: () => StatelessStateStore,
|
|
42
|
-
TokenExchangeError: () =>
|
|
47
|
+
TokenExchangeError: () => import_auth0_auth_js4.TokenExchangeError,
|
|
43
48
|
isMfaRequiredError: () => import_auth0_auth_js2.isMfaRequiredError
|
|
44
49
|
});
|
|
45
50
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -168,6 +173,18 @@ function updateStateDataForConnectionTokenSet(options, stateData, tokenEndpointR
|
|
|
168
173
|
var import_auth0_auth_js = require("@auth0/auth0-auth-js");
|
|
169
174
|
|
|
170
175
|
// src/utils.ts
|
|
176
|
+
var DEFAULT_SCOPES = "openid profile email offline_access";
|
|
177
|
+
var ensureOpenIdScope = (scope) => {
|
|
178
|
+
const normalizedScope = scope?.trim();
|
|
179
|
+
if (!normalizedScope) {
|
|
180
|
+
return DEFAULT_SCOPES;
|
|
181
|
+
}
|
|
182
|
+
const scopes = normalizedScope.split(/\s+/);
|
|
183
|
+
if (!scopes.includes("openid")) {
|
|
184
|
+
scopes.unshift("openid");
|
|
185
|
+
}
|
|
186
|
+
return scopes.join(" ");
|
|
187
|
+
};
|
|
171
188
|
var compareScopes = (scopes, requiredScopes) => {
|
|
172
189
|
if (scopes === requiredScopes) {
|
|
173
190
|
return true;
|
|
@@ -191,7 +208,7 @@ function getTelemetryConfig(config) {
|
|
|
191
208
|
return {
|
|
192
209
|
enabled: true,
|
|
193
210
|
name: config?.name ?? "@auth0/auth0-server-js",
|
|
194
|
-
version: config?.version ?? "1.
|
|
211
|
+
version: config?.version ?? "1.7.0"
|
|
195
212
|
};
|
|
196
213
|
}
|
|
197
214
|
|
|
@@ -275,8 +292,99 @@ var ServerMfaClient = class {
|
|
|
275
292
|
}
|
|
276
293
|
};
|
|
277
294
|
|
|
295
|
+
// src/passkey/server-passkey-client.ts
|
|
296
|
+
var ServerPasskeyClient = class {
|
|
297
|
+
#options;
|
|
298
|
+
/**
|
|
299
|
+
* @internal
|
|
300
|
+
*/
|
|
301
|
+
constructor(options) {
|
|
302
|
+
this.#options = options;
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* Requests a passkey signup challenge for a new user.
|
|
306
|
+
*
|
|
307
|
+
* Returns the `authSession` and the WebAuthn credential creation options
|
|
308
|
+
* (`authnParamsPublicKey`). The application must return these to the browser,
|
|
309
|
+
* pass `authnParamsPublicKey` to `navigator.credentials.create()`, and then
|
|
310
|
+
* call `getToken()` with the resulting credential to complete signup.
|
|
311
|
+
*
|
|
312
|
+
* This method does not create a session; no state is persisted.
|
|
313
|
+
*
|
|
314
|
+
* @param options User profile data and optional realm/organization.
|
|
315
|
+
* @param storeOptions Optional options used to resolve the domain (resolver mode).
|
|
316
|
+
*
|
|
317
|
+
* @throws {PasskeyRegisterError} If there was an issue requesting the signup challenge.
|
|
318
|
+
*
|
|
319
|
+
* @returns A promise resolving to the signup challenge.
|
|
320
|
+
*/
|
|
321
|
+
async register(options, storeOptions) {
|
|
322
|
+
const domain = await this.#options.resolveDomain(storeOptions);
|
|
323
|
+
const authClient = this.#options.getAuthClient(domain);
|
|
324
|
+
return authClient.passkey.register(options);
|
|
325
|
+
}
|
|
326
|
+
/**
|
|
327
|
+
* Requests a passkey login challenge for an existing user.
|
|
328
|
+
*
|
|
329
|
+
* Returns the `authSession` and the WebAuthn credential request options
|
|
330
|
+
* (`authnParamsPublicKey`). The application must return these to the browser,
|
|
331
|
+
* pass `authnParamsPublicKey` to `navigator.credentials.get()`, and then
|
|
332
|
+
* call `getToken()` with the resulting credential to complete login.
|
|
333
|
+
*
|
|
334
|
+
* This method does not create a session; no state is persisted.
|
|
335
|
+
*
|
|
336
|
+
* @param options Optional realm/organization configuration.
|
|
337
|
+
* @param storeOptions Optional options used to resolve the domain (resolver mode).
|
|
338
|
+
*
|
|
339
|
+
* @throws {PasskeyChallengeError} If there was an issue requesting the login challenge.
|
|
340
|
+
*
|
|
341
|
+
* @returns A promise resolving to the login challenge.
|
|
342
|
+
*/
|
|
343
|
+
async challenge(options, storeOptions) {
|
|
344
|
+
const domain = await this.#options.resolveDomain(storeOptions);
|
|
345
|
+
const authClient = this.#options.getAuthClient(domain);
|
|
346
|
+
return authClient.passkey.challenge(options);
|
|
347
|
+
}
|
|
348
|
+
/**
|
|
349
|
+
* Completes a passkey authentication flow (signup or login) by exchanging the
|
|
350
|
+
* WebAuthn credential for tokens, and persists the resulting session.
|
|
351
|
+
*
|
|
352
|
+
* Call this after obtaining a credential from `navigator.credentials.create()`
|
|
353
|
+
* (signup) or `navigator.credentials.get()` (login), passing the `authSession`
|
|
354
|
+
* returned by `register()` / `challenge()` together with the serialized credential.
|
|
355
|
+
*
|
|
356
|
+
* In resolver (multi-tenant) mode, pass the same `storeOptions` you passed to
|
|
357
|
+
* `register()` / `challenge()` so the token exchange resolves the same tenant
|
|
358
|
+
* that issued the `authSession`; otherwise the exchange will fail.
|
|
359
|
+
*
|
|
360
|
+
* @param options The auth session, serialized credential, and optional realm/scope/audience/organization.
|
|
361
|
+
* @param storeOptions Optional options used to pass to the State Store (and to resolve the domain in resolver mode).
|
|
362
|
+
*
|
|
363
|
+
* @throws {PasskeyGetTokenError} If there was an issue exchanging the credential for tokens. When the cause is `mfa_required`, use `isMfaRequiredError(error)` to narrow the error and read `cause.mfa_token`. No session is persisted in this case.
|
|
364
|
+
* @throws {OrganizationValidationError} When `organization` is passed and the returned ID token's organization claim is missing or does not match. The error is thrown before the session is written, so no session is persisted in this case.
|
|
365
|
+
*
|
|
366
|
+
* @returns A promise resolving to an object containing the authorizationDetails (when RAR was used).
|
|
367
|
+
*/
|
|
368
|
+
async getToken(options, storeOptions) {
|
|
369
|
+
const scope = ensureOpenIdScope(options.scope ?? this.#options.defaultScope);
|
|
370
|
+
const audience = options.audience ?? this.#options.defaultAudience;
|
|
371
|
+
const domain = await this.#options.resolveDomain(storeOptions);
|
|
372
|
+
const authClient = this.#options.getAuthClient(domain);
|
|
373
|
+
const tokenEndpointResponse = await authClient.passkey.getTokenByPasskey({
|
|
374
|
+
...options,
|
|
375
|
+
scope,
|
|
376
|
+
audience
|
|
377
|
+
});
|
|
378
|
+
const existingStateData = await this.#options.stateStore.get(this.#options.stateStoreIdentifier, storeOptions);
|
|
379
|
+
const stateData = updateStateData(audience ?? "default", existingStateData, tokenEndpointResponse, { domain });
|
|
380
|
+
await this.#options.stateStore.set(this.#options.stateStoreIdentifier, stateData, true, storeOptions);
|
|
381
|
+
return {
|
|
382
|
+
authorizationDetails: tokenEndpointResponse.authorizationDetails
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
};
|
|
386
|
+
|
|
278
387
|
// src/server-client.ts
|
|
279
|
-
var DEFAULT_SCOPES = "openid profile email offline_access";
|
|
280
388
|
var normalizeDomain = (value) => {
|
|
281
389
|
const trimmed = value.trim();
|
|
282
390
|
const parsed = trimmed.startsWith("http") ? new URL(trimmed) : new URL(`https://${trimmed}`);
|
|
@@ -290,16 +398,6 @@ var decodeIssuer = (token) => {
|
|
|
290
398
|
return void 0;
|
|
291
399
|
}
|
|
292
400
|
};
|
|
293
|
-
var ensureOpenIdScope = (scope) => {
|
|
294
|
-
if (!scope) {
|
|
295
|
-
return DEFAULT_SCOPES;
|
|
296
|
-
}
|
|
297
|
-
const scopes = scope.split(" ");
|
|
298
|
-
if (!scopes.includes("openid")) {
|
|
299
|
-
scopes.unshift("openid");
|
|
300
|
-
}
|
|
301
|
-
return scopes.join(" ");
|
|
302
|
-
};
|
|
303
401
|
var ServerClient = class {
|
|
304
402
|
#options;
|
|
305
403
|
#transactionStore;
|
|
@@ -310,6 +408,7 @@ var ServerClient = class {
|
|
|
310
408
|
#staticDomain;
|
|
311
409
|
#authClient;
|
|
312
410
|
#mfaClient;
|
|
411
|
+
#passkeyClient;
|
|
313
412
|
/**
|
|
314
413
|
* The underlying `authClient` instance that can be used to interact with the Auth0 Authentication API.
|
|
315
414
|
* Generally, you should prefer to use the higher-level methods exposed on the `ServerClient` instance.
|
|
@@ -344,6 +443,19 @@ var ServerClient = class {
|
|
|
344
443
|
}
|
|
345
444
|
return this.#mfaClient;
|
|
346
445
|
}
|
|
446
|
+
/**
|
|
447
|
+
* The passkey client for signing up and logging in users with WebAuthn credentials.
|
|
448
|
+
*
|
|
449
|
+
* Provides `register()` and `challenge()` to request signup/login challenges, and
|
|
450
|
+
* `getToken()` to exchange the resulting credential for tokens and persist the session.
|
|
451
|
+
*
|
|
452
|
+
* Unlike `mfa`, this property is available in both static and resolver (multi-tenant)
|
|
453
|
+
* domain modes. In resolver mode, pass the same `storeOptions` to `register()`/`challenge()`
|
|
454
|
+
* and `getToken()` so the credential is exchanged against the tenant that issued it.
|
|
455
|
+
*/
|
|
456
|
+
get passkey() {
|
|
457
|
+
return this.#passkeyClient;
|
|
458
|
+
}
|
|
347
459
|
constructor(options) {
|
|
348
460
|
this.#options = options;
|
|
349
461
|
this.#stateStoreIdentifier = this.#options.stateIdentifier || "__a0_session";
|
|
@@ -385,6 +497,14 @@ var ServerClient = class {
|
|
|
385
497
|
defaultAudience: this.#options.authorizationParams?.audience ?? "default"
|
|
386
498
|
});
|
|
387
499
|
}
|
|
500
|
+
this.#passkeyClient = new ServerPasskeyClient({
|
|
501
|
+
resolveDomain: (storeOptions) => this.#resolveDomain(storeOptions),
|
|
502
|
+
getAuthClient: (domain) => this.#getAuthClient(domain),
|
|
503
|
+
stateStore: this.#stateStore,
|
|
504
|
+
stateStoreIdentifier: this.#stateStoreIdentifier,
|
|
505
|
+
defaultScope: this.#options.authorizationParams?.scope,
|
|
506
|
+
defaultAudience: this.#options.authorizationParams?.audience
|
|
507
|
+
});
|
|
388
508
|
}
|
|
389
509
|
async #resolveDomain(storeOptions) {
|
|
390
510
|
if (typeof this.#options.domain === "function") {
|
|
@@ -489,6 +609,7 @@ var ServerClient = class {
|
|
|
489
609
|
const domain = transactionData.domain ?? await this.#resolveDomain(storeOptions);
|
|
490
610
|
const authClient = this.#getAuthClient(domain);
|
|
491
611
|
const tokenEndpointResponse = await authClient.getTokenByCode(url, {
|
|
612
|
+
// TransactionData.codeVerifier is optional only to accommodate magic-link transactions.
|
|
492
613
|
codeVerifier: transactionData.codeVerifier
|
|
493
614
|
});
|
|
494
615
|
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
@@ -652,6 +773,178 @@ var ServerClient = class {
|
|
|
652
773
|
authorizationDetails: tokenEndpointResponse.authorizationDetails
|
|
653
774
|
};
|
|
654
775
|
}
|
|
776
|
+
/**
|
|
777
|
+
* Starts a passwordless flow by sending a one-time code (OTP) or a magic link.
|
|
778
|
+
*
|
|
779
|
+
* Discriminated on `connection` (and, for email, `send`) to mirror the
|
|
780
|
+
* `@auth0/nextjs-auth0` `passwordless.start()` surface:
|
|
781
|
+
* - `{ connection: 'email' }` / `{ connection: 'email', send: 'code' }` — email OTP
|
|
782
|
+
* - `{ connection: 'email', send: 'link', redirectUri }` — email magic link
|
|
783
|
+
* - `{ connection: 'sms' }` — SMS OTP
|
|
784
|
+
*
|
|
785
|
+
* OTP modes are a stateless passthrough to the Authentication API (no session, no transaction);
|
|
786
|
+
* complete them with {@link ServerClient#completePasswordless}.
|
|
787
|
+
*
|
|
788
|
+
* Magic-link mode is stateful: the SDK generates an opaque anti-forgery `state`, sends the link
|
|
789
|
+
* with the OAuth parameters embedded (`redirect_uri`, `response_type=code`, `scope`, `state`),
|
|
790
|
+
* and persists a transaction carrying that `state`. NO PKCE challenge is registered, so the
|
|
791
|
+
* transaction holds no `codeVerifier`. Complete it with
|
|
792
|
+
* {@link ServerClient#completePasswordlessMagicLink}. Requires the tenant setting
|
|
793
|
+
* `allow_magiclink_verify_without_session: true` for server-side completion.
|
|
794
|
+
*
|
|
795
|
+
* @param options Discriminated start options.
|
|
796
|
+
* @param storeOptions Optional options passed to the resolver / stores.
|
|
797
|
+
*
|
|
798
|
+
* @throws {PasswordlessStartError} If the request fails, or if a magic link is requested without a `redirectUri`.
|
|
799
|
+
*
|
|
800
|
+
* @example
|
|
801
|
+
* // Email OTP
|
|
802
|
+
* await serverClient.startPasswordless({ connection: 'email', email: 'user@example.com' });
|
|
803
|
+
* // SMS OTP
|
|
804
|
+
* await serverClient.startPasswordless({ connection: 'sms', phoneNumber: '+14155550100' });
|
|
805
|
+
* // Email magic link
|
|
806
|
+
* await serverClient.startPasswordless({
|
|
807
|
+
* connection: 'email',
|
|
808
|
+
* email: 'user@example.com',
|
|
809
|
+
* send: 'link',
|
|
810
|
+
* redirectUri: 'https://app.example.com/auth/callback',
|
|
811
|
+
* });
|
|
812
|
+
*/
|
|
813
|
+
async startPasswordless(options, storeOptions) {
|
|
814
|
+
const domain = await this.#resolveDomain(storeOptions);
|
|
815
|
+
const authClient = this.#getAuthClient(domain);
|
|
816
|
+
if (options.connection === "sms") {
|
|
817
|
+
await authClient.passwordless.sendSms({
|
|
818
|
+
phoneNumber: options.phoneNumber,
|
|
819
|
+
language: options.language
|
|
820
|
+
});
|
|
821
|
+
return;
|
|
822
|
+
}
|
|
823
|
+
if (options.send !== "link") {
|
|
824
|
+
await authClient.passwordless.sendEmail({
|
|
825
|
+
email: options.email,
|
|
826
|
+
send: "code",
|
|
827
|
+
language: options.language
|
|
828
|
+
});
|
|
829
|
+
return;
|
|
830
|
+
}
|
|
831
|
+
if (!options.redirectUri || typeof options.redirectUri !== "string") {
|
|
832
|
+
throw new import_auth0_auth_js.PasswordlessStartError("redirectUri is required to start a passwordless magic-link login.");
|
|
833
|
+
}
|
|
834
|
+
const state = crypto.randomUUID();
|
|
835
|
+
const scope = ensureOpenIdScope(options.scope ?? this.#options.authorizationParams?.scope);
|
|
836
|
+
const audience = options.audience ?? this.#options.authorizationParams?.audience;
|
|
837
|
+
await authClient.passwordless.sendEmail({
|
|
838
|
+
email: options.email,
|
|
839
|
+
send: "link",
|
|
840
|
+
language: options.language,
|
|
841
|
+
authParams: {
|
|
842
|
+
...options.authParams,
|
|
843
|
+
redirect_uri: options.redirectUri,
|
|
844
|
+
response_type: "code",
|
|
845
|
+
scope,
|
|
846
|
+
...audience ? { audience } : {},
|
|
847
|
+
state
|
|
848
|
+
}
|
|
849
|
+
});
|
|
850
|
+
const transactionState = {
|
|
851
|
+
audience,
|
|
852
|
+
domain,
|
|
853
|
+
state
|
|
854
|
+
};
|
|
855
|
+
await this.#transactionStore.set(this.#transactionStoreIdentifier, transactionState, false, storeOptions);
|
|
856
|
+
}
|
|
857
|
+
/**
|
|
858
|
+
* Completes a passwordless OTP login and persists the resulting session.
|
|
859
|
+
*
|
|
860
|
+
* Discriminated on `connection` to mirror the `@auth0/nextjs-auth0` `passwordless.verify()`
|
|
861
|
+
* surface. Non-redirect flow: no PKCE and no transaction store (mirrors
|
|
862
|
+
* {@link ServerClient#loginBackchannel}). The `openid` scope is always ensured by this layer.
|
|
863
|
+
*
|
|
864
|
+
* Note: the state store is read-then-written; if your deployment performs concurrent
|
|
865
|
+
* logins for the same session identifier, use a state store with atomic/serializable
|
|
866
|
+
* writes to avoid last-write-wins races.
|
|
867
|
+
*
|
|
868
|
+
* @param options Discriminated completion options (`connection`, identifier, `verificationCode`).
|
|
869
|
+
* @param storeOptions Optional options passed to the resolver / stores.
|
|
870
|
+
*
|
|
871
|
+
* @throws {PasswordlessVerifyError} If the code is invalid, expired, or rate-limited. When the
|
|
872
|
+
* connection requires MFA, the server responds with `mfa_required`; narrow the thrown error
|
|
873
|
+
* with `isMfaRequiredError(error)` to read `cause.mfa_token`.
|
|
874
|
+
*
|
|
875
|
+
* @returns A promise resolving to the authorizationDetails (when RAR was used).
|
|
876
|
+
*/
|
|
877
|
+
async completePasswordless(options, storeOptions) {
|
|
878
|
+
const scope = ensureOpenIdScope(options.authorizationParams?.scope ?? this.#options.authorizationParams?.scope);
|
|
879
|
+
const audience = options.authorizationParams?.audience ?? this.#options.authorizationParams?.audience;
|
|
880
|
+
const domain = await this.#resolveDomain(storeOptions);
|
|
881
|
+
const authClient = this.#getAuthClient(domain);
|
|
882
|
+
const tokenEndpointResponse = options.connection === "sms" ? await authClient.getTokenByPasswordlessSms({
|
|
883
|
+
phoneNumber: options.phoneNumber,
|
|
884
|
+
code: options.verificationCode,
|
|
885
|
+
audience,
|
|
886
|
+
scope
|
|
887
|
+
}) : await authClient.getTokenByPasswordlessEmail({
|
|
888
|
+
email: options.email,
|
|
889
|
+
code: options.verificationCode,
|
|
890
|
+
audience,
|
|
891
|
+
scope
|
|
892
|
+
});
|
|
893
|
+
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
894
|
+
const stateData = updateStateData(
|
|
895
|
+
this.#options.authorizationParams?.audience ?? "default",
|
|
896
|
+
existingStateData,
|
|
897
|
+
tokenEndpointResponse,
|
|
898
|
+
{ domain }
|
|
899
|
+
);
|
|
900
|
+
await this.#stateStore.set(this.#stateStoreIdentifier, stateData, true, storeOptions);
|
|
901
|
+
return {
|
|
902
|
+
authorizationDetails: tokenEndpointResponse.authorizationDetails
|
|
903
|
+
};
|
|
904
|
+
}
|
|
905
|
+
/**
|
|
906
|
+
* Completes a passwordless magic-link login and persists the resulting session.
|
|
907
|
+
*
|
|
908
|
+
* Loads the transaction persisted by {@link ServerClient#startPasswordless} (magic-link mode), validates the
|
|
909
|
+
* `state` returned on the callback URL against the stored `state` (anti-forgery binding), exchanges
|
|
910
|
+
* the authorization code WITHOUT PKCE, writes the session, and deletes the transaction. The existing
|
|
911
|
+
* interactive login path ({@link ServerClient#completeInteractiveLogin}) is not used.
|
|
912
|
+
*
|
|
913
|
+
* @param url The callback URL containing the authorization `code` and `state`.
|
|
914
|
+
* @param storeOptions Optional options passed to the resolver / stores.
|
|
915
|
+
*
|
|
916
|
+
* @throws {MissingTransactionError} If no magic-link transaction was found.
|
|
917
|
+
* @throws {PasswordlessVerifyError} If the returned `state` is missing or does not match.
|
|
918
|
+
* @throws {TokenByCodeError} If the token exchange fails.
|
|
919
|
+
*
|
|
920
|
+
* @returns A promise resolving to the authorizationDetails (when RAR was used).
|
|
921
|
+
*
|
|
922
|
+
* @example
|
|
923
|
+
* const result = await serverClient.completePasswordlessMagicLink(callbackUrl, storeOptions);
|
|
924
|
+
*/
|
|
925
|
+
async completePasswordlessMagicLink(url, storeOptions) {
|
|
926
|
+
const transactionData = await this.#transactionStore.get(this.#transactionStoreIdentifier, storeOptions);
|
|
927
|
+
if (!transactionData) {
|
|
928
|
+
throw new MissingTransactionError();
|
|
929
|
+
}
|
|
930
|
+
const expectedState = typeof transactionData.state === "string" ? transactionData.state : void 0;
|
|
931
|
+
const returnedState = url.searchParams.get("state");
|
|
932
|
+
if (!returnedState || !expectedState || returnedState !== expectedState) {
|
|
933
|
+
throw new import_auth0_auth_js.PasswordlessVerifyError("State mismatch on magic-link callback");
|
|
934
|
+
}
|
|
935
|
+
const domain = transactionData.domain ?? await this.#resolveDomain(storeOptions);
|
|
936
|
+
const authClient = this.#getAuthClient(domain);
|
|
937
|
+
const tokenEndpointResponse = await authClient.getTokenByMagicLinkCode(url, { expectedState });
|
|
938
|
+
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
939
|
+
const stateData = updateStateData(transactionData.audience ?? "default", existingStateData, tokenEndpointResponse, {
|
|
940
|
+
domain
|
|
941
|
+
});
|
|
942
|
+
await this.#stateStore.set(this.#stateStoreIdentifier, stateData, true, storeOptions);
|
|
943
|
+
await this.#transactionStore.delete(this.#transactionStoreIdentifier, storeOptions);
|
|
944
|
+
return {
|
|
945
|
+
authorizationDetails: tokenEndpointResponse.authorizationDetails
|
|
946
|
+
};
|
|
947
|
+
}
|
|
655
948
|
/**
|
|
656
949
|
* Retrieves the user from the store, or undefined if no user found.
|
|
657
950
|
* @param storeOptions Optional options used to pass to the Transaction and State Store.
|
|
@@ -691,16 +984,29 @@ var ServerClient = class {
|
|
|
691
984
|
/**
|
|
692
985
|
* 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.
|
|
693
986
|
* Also updates the store when a new token was retrieved from Auth0.
|
|
987
|
+
*
|
|
988
|
+
* When `options.audience` and/or `options.scope` are provided, the SDK uses the session's refresh token to
|
|
989
|
+
* request an access token for that audience/scope (Multi-Resource Refresh Tokens). Tokens are cached per
|
|
990
|
+
* audience and scope combination.
|
|
991
|
+
*
|
|
992
|
+
* @param options Optional options for requesting a specific audience/scope.
|
|
694
993
|
* @param storeOptions Optional options used to pass to the Transaction and State Store.
|
|
695
994
|
*
|
|
696
995
|
* @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`.
|
|
697
996
|
*
|
|
698
997
|
* @returns The Token Set, containing the access token, as well as additional information.
|
|
699
998
|
*/
|
|
700
|
-
async getAccessToken(storeOptions) {
|
|
701
|
-
const
|
|
702
|
-
|
|
703
|
-
|
|
999
|
+
async getAccessToken(tokenOptionsOrStoreOptions, storeOptions) {
|
|
1000
|
+
const hasTokenOptions = (
|
|
1001
|
+
// If second arg exists, first arg must be GetAccessTokenOptions
|
|
1002
|
+
storeOptions !== void 0 || // OR if first arg has audience/scope properties
|
|
1003
|
+
!!tokenOptionsOrStoreOptions && typeof tokenOptionsOrStoreOptions === "object" && ("audience" in tokenOptionsOrStoreOptions || "scope" in tokenOptionsOrStoreOptions)
|
|
1004
|
+
);
|
|
1005
|
+
const [resolvedOptions, resolvedStoreOptions] = hasTokenOptions ? [tokenOptionsOrStoreOptions, storeOptions] : [void 0, tokenOptionsOrStoreOptions];
|
|
1006
|
+
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, resolvedStoreOptions);
|
|
1007
|
+
const requestedAudience = resolvedOptions?.audience ?? this.#options.authorizationParams?.audience;
|
|
1008
|
+
const audience = requestedAudience ?? "default";
|
|
1009
|
+
const scope = resolvedOptions?.scope ?? this.#options.authorizationParams?.scope;
|
|
704
1010
|
const sessionDomain = stateData ? this.#getSessionDomain(stateData) : this.#staticDomain;
|
|
705
1011
|
if (this.#isResolverMode()) {
|
|
706
1012
|
if (!stateData) {
|
|
@@ -709,7 +1015,7 @@ var ServerClient = class {
|
|
|
709
1015
|
if (!sessionDomain) {
|
|
710
1016
|
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
711
1017
|
}
|
|
712
|
-
const resolvedDomain = await this.#resolveDomain(
|
|
1018
|
+
const resolvedDomain = await this.#resolveDomain(resolvedStoreOptions);
|
|
713
1019
|
if (sessionDomain !== resolvedDomain) {
|
|
714
1020
|
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
715
1021
|
}
|
|
@@ -726,14 +1032,21 @@ var ServerClient = class {
|
|
|
726
1032
|
);
|
|
727
1033
|
}
|
|
728
1034
|
const domainForSession = sessionDomain;
|
|
729
|
-
const
|
|
730
|
-
refreshToken: stateData.refreshToken
|
|
731
|
-
|
|
732
|
-
|
|
1035
|
+
const tokenByRefreshTokenOptions = {
|
|
1036
|
+
refreshToken: stateData.refreshToken,
|
|
1037
|
+
// Only forward audience/scope to Auth0 when token options were explicitly supplied, and
|
|
1038
|
+
// never send the synthetic 'default' cache-key audience as a real request parameter.
|
|
1039
|
+
...hasTokenOptions && {
|
|
1040
|
+
...requestedAudience && { audience: requestedAudience },
|
|
1041
|
+
...scope && { scope }
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
const tokenEndpointResponse = await this.#getAuthClient(domainForSession).getTokenByRefreshToken(tokenByRefreshTokenOptions);
|
|
1045
|
+
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, resolvedStoreOptions);
|
|
733
1046
|
const updatedStateData = updateStateData(audience, existingStateData, tokenEndpointResponse, {
|
|
734
1047
|
domain: domainForSession
|
|
735
1048
|
});
|
|
736
|
-
await this.#stateStore.set(this.#stateStoreIdentifier, updatedStateData, false,
|
|
1049
|
+
await this.#stateStore.set(this.#stateStoreIdentifier, updatedStateData, false, resolvedStoreOptions);
|
|
737
1050
|
return {
|
|
738
1051
|
accessToken: tokenEndpointResponse.accessToken,
|
|
739
1052
|
scope: tokenEndpointResponse.scope,
|
|
@@ -1041,7 +1354,7 @@ var AbstractTransactionStore = class extends AbstractStore {
|
|
|
1041
1354
|
};
|
|
1042
1355
|
|
|
1043
1356
|
// src/index.ts
|
|
1044
|
-
var
|
|
1357
|
+
var import_auth0_auth_js4 = require("@auth0/auth0-auth-js");
|
|
1045
1358
|
|
|
1046
1359
|
// src/store/cookie-transaction-store.ts
|
|
1047
1360
|
var CookieTransactionStore = class extends AbstractTransactionStore {
|
|
@@ -1239,6 +1552,9 @@ var StatelessStateStore = class extends AbstractSessionStore {
|
|
|
1239
1552
|
|
|
1240
1553
|
// src/mfa/index.ts
|
|
1241
1554
|
var import_auth0_auth_js2 = require("@auth0/auth0-auth-js");
|
|
1555
|
+
|
|
1556
|
+
// src/passkey/index.ts
|
|
1557
|
+
var import_auth0_auth_js3 = require("@auth0/auth0-auth-js");
|
|
1242
1558
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1243
1559
|
0 && (module.exports = {
|
|
1244
1560
|
AbstractStateStore,
|
|
@@ -1255,8 +1571,13 @@ var import_auth0_auth_js2 = require("@auth0/auth0-auth-js");
|
|
|
1255
1571
|
MissingRequiredArgumentError,
|
|
1256
1572
|
MissingSessionError,
|
|
1257
1573
|
MissingTransactionError,
|
|
1574
|
+
OrganizationValidationError,
|
|
1575
|
+
PasskeyChallengeError,
|
|
1576
|
+
PasskeyGetTokenError,
|
|
1577
|
+
PasskeyRegisterError,
|
|
1258
1578
|
ServerClient,
|
|
1259
1579
|
ServerMfaClient,
|
|
1580
|
+
ServerPasskeyClient,
|
|
1260
1581
|
StartLinkUserError,
|
|
1261
1582
|
StatefulStateStore,
|
|
1262
1583
|
StatelessStateStore,
|