@auth0/auth0-server-js 1.6.0 → 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 +350 -22
- 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 +350 -22
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -30,14 +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: () => import_auth0_auth_js4.MissingClientAuthError,
|
|
33
34
|
MissingRequiredArgumentError: () => MissingRequiredArgumentError,
|
|
34
35
|
MissingSessionError: () => MissingSessionError,
|
|
35
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,
|
|
36
41
|
ServerClient: () => ServerClient,
|
|
37
42
|
ServerMfaClient: () => ServerMfaClient,
|
|
43
|
+
ServerPasskeyClient: () => ServerPasskeyClient,
|
|
38
44
|
StartLinkUserError: () => StartLinkUserError,
|
|
39
45
|
StatefulStateStore: () => StatefulStateStore,
|
|
40
46
|
StatelessStateStore: () => StatelessStateStore,
|
|
47
|
+
TokenExchangeError: () => import_auth0_auth_js4.TokenExchangeError,
|
|
41
48
|
isMfaRequiredError: () => import_auth0_auth_js2.isMfaRequiredError
|
|
42
49
|
});
|
|
43
50
|
module.exports = __toCommonJS(index_exports);
|
|
@@ -166,6 +173,18 @@ function updateStateDataForConnectionTokenSet(options, stateData, tokenEndpointR
|
|
|
166
173
|
var import_auth0_auth_js = require("@auth0/auth0-auth-js");
|
|
167
174
|
|
|
168
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
|
+
};
|
|
169
188
|
var compareScopes = (scopes, requiredScopes) => {
|
|
170
189
|
if (scopes === requiredScopes) {
|
|
171
190
|
return true;
|
|
@@ -189,7 +208,7 @@ function getTelemetryConfig(config) {
|
|
|
189
208
|
return {
|
|
190
209
|
enabled: true,
|
|
191
210
|
name: config?.name ?? "@auth0/auth0-server-js",
|
|
192
|
-
version: config?.version ?? "1.
|
|
211
|
+
version: config?.version ?? "1.7.0"
|
|
193
212
|
};
|
|
194
213
|
}
|
|
195
214
|
|
|
@@ -273,8 +292,99 @@ var ServerMfaClient = class {
|
|
|
273
292
|
}
|
|
274
293
|
};
|
|
275
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
|
+
|
|
276
387
|
// src/server-client.ts
|
|
277
|
-
var DEFAULT_SCOPES = "openid profile email offline_access";
|
|
278
388
|
var normalizeDomain = (value) => {
|
|
279
389
|
const trimmed = value.trim();
|
|
280
390
|
const parsed = trimmed.startsWith("http") ? new URL(trimmed) : new URL(`https://${trimmed}`);
|
|
@@ -288,16 +398,6 @@ var decodeIssuer = (token) => {
|
|
|
288
398
|
return void 0;
|
|
289
399
|
}
|
|
290
400
|
};
|
|
291
|
-
var ensureOpenIdScope = (scope) => {
|
|
292
|
-
if (!scope) {
|
|
293
|
-
return DEFAULT_SCOPES;
|
|
294
|
-
}
|
|
295
|
-
const scopes = scope.split(" ");
|
|
296
|
-
if (!scopes.includes("openid")) {
|
|
297
|
-
scopes.unshift("openid");
|
|
298
|
-
}
|
|
299
|
-
return scopes.join(" ");
|
|
300
|
-
};
|
|
301
401
|
var ServerClient = class {
|
|
302
402
|
#options;
|
|
303
403
|
#transactionStore;
|
|
@@ -308,6 +408,7 @@ var ServerClient = class {
|
|
|
308
408
|
#staticDomain;
|
|
309
409
|
#authClient;
|
|
310
410
|
#mfaClient;
|
|
411
|
+
#passkeyClient;
|
|
311
412
|
/**
|
|
312
413
|
* The underlying `authClient` instance that can be used to interact with the Auth0 Authentication API.
|
|
313
414
|
* Generally, you should prefer to use the higher-level methods exposed on the `ServerClient` instance.
|
|
@@ -342,6 +443,19 @@ var ServerClient = class {
|
|
|
342
443
|
}
|
|
343
444
|
return this.#mfaClient;
|
|
344
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
|
+
}
|
|
345
459
|
constructor(options) {
|
|
346
460
|
this.#options = options;
|
|
347
461
|
this.#stateStoreIdentifier = this.#options.stateIdentifier || "__a0_session";
|
|
@@ -383,6 +497,14 @@ var ServerClient = class {
|
|
|
383
497
|
defaultAudience: this.#options.authorizationParams?.audience ?? "default"
|
|
384
498
|
});
|
|
385
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
|
+
});
|
|
386
508
|
}
|
|
387
509
|
async #resolveDomain(storeOptions) {
|
|
388
510
|
if (typeof this.#options.domain === "function") {
|
|
@@ -487,6 +609,7 @@ var ServerClient = class {
|
|
|
487
609
|
const domain = transactionData.domain ?? await this.#resolveDomain(storeOptions);
|
|
488
610
|
const authClient = this.#getAuthClient(domain);
|
|
489
611
|
const tokenEndpointResponse = await authClient.getTokenByCode(url, {
|
|
612
|
+
// TransactionData.codeVerifier is optional only to accommodate magic-link transactions.
|
|
490
613
|
codeVerifier: transactionData.codeVerifier
|
|
491
614
|
});
|
|
492
615
|
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
@@ -650,6 +773,178 @@ var ServerClient = class {
|
|
|
650
773
|
authorizationDetails: tokenEndpointResponse.authorizationDetails
|
|
651
774
|
};
|
|
652
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
|
+
}
|
|
653
948
|
/**
|
|
654
949
|
* Retrieves the user from the store, or undefined if no user found.
|
|
655
950
|
* @param storeOptions Optional options used to pass to the Transaction and State Store.
|
|
@@ -689,16 +984,29 @@ var ServerClient = class {
|
|
|
689
984
|
/**
|
|
690
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.
|
|
691
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.
|
|
692
993
|
* @param storeOptions Optional options used to pass to the Transaction and State Store.
|
|
693
994
|
*
|
|
694
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`.
|
|
695
996
|
*
|
|
696
997
|
* @returns The Token Set, containing the access token, as well as additional information.
|
|
697
998
|
*/
|
|
698
|
-
async getAccessToken(storeOptions) {
|
|
699
|
-
const
|
|
700
|
-
|
|
701
|
-
|
|
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;
|
|
702
1010
|
const sessionDomain = stateData ? this.#getSessionDomain(stateData) : this.#staticDomain;
|
|
703
1011
|
if (this.#isResolverMode()) {
|
|
704
1012
|
if (!stateData) {
|
|
@@ -707,7 +1015,7 @@ var ServerClient = class {
|
|
|
707
1015
|
if (!sessionDomain) {
|
|
708
1016
|
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
709
1017
|
}
|
|
710
|
-
const resolvedDomain = await this.#resolveDomain(
|
|
1018
|
+
const resolvedDomain = await this.#resolveDomain(resolvedStoreOptions);
|
|
711
1019
|
if (sessionDomain !== resolvedDomain) {
|
|
712
1020
|
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
713
1021
|
}
|
|
@@ -724,14 +1032,21 @@ var ServerClient = class {
|
|
|
724
1032
|
);
|
|
725
1033
|
}
|
|
726
1034
|
const domainForSession = sessionDomain;
|
|
727
|
-
const
|
|
728
|
-
refreshToken: stateData.refreshToken
|
|
729
|
-
|
|
730
|
-
|
|
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);
|
|
731
1046
|
const updatedStateData = updateStateData(audience, existingStateData, tokenEndpointResponse, {
|
|
732
1047
|
domain: domainForSession
|
|
733
1048
|
});
|
|
734
|
-
await this.#stateStore.set(this.#stateStoreIdentifier, updatedStateData, false,
|
|
1049
|
+
await this.#stateStore.set(this.#stateStoreIdentifier, updatedStateData, false, resolvedStoreOptions);
|
|
735
1050
|
return {
|
|
736
1051
|
accessToken: tokenEndpointResponse.accessToken,
|
|
737
1052
|
scope: tokenEndpointResponse.scope,
|
|
@@ -1038,6 +1353,9 @@ var AbstractTransactionStore = class extends AbstractStore {
|
|
|
1038
1353
|
}
|
|
1039
1354
|
};
|
|
1040
1355
|
|
|
1356
|
+
// src/index.ts
|
|
1357
|
+
var import_auth0_auth_js4 = require("@auth0/auth0-auth-js");
|
|
1358
|
+
|
|
1041
1359
|
// src/store/cookie-transaction-store.ts
|
|
1042
1360
|
var CookieTransactionStore = class extends AbstractTransactionStore {
|
|
1043
1361
|
#cookieHandler;
|
|
@@ -1234,6 +1552,9 @@ var StatelessStateStore = class extends AbstractSessionStore {
|
|
|
1234
1552
|
|
|
1235
1553
|
// src/mfa/index.ts
|
|
1236
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");
|
|
1237
1558
|
// Annotate the CommonJS export names for ESM import in node:
|
|
1238
1559
|
0 && (module.exports = {
|
|
1239
1560
|
AbstractStateStore,
|
|
@@ -1246,14 +1567,21 @@ var import_auth0_auth_js2 = require("@auth0/auth0-auth-js");
|
|
|
1246
1567
|
MfaEnrollmentError,
|
|
1247
1568
|
MfaListAuthenticatorsError,
|
|
1248
1569
|
MfaVerifyError,
|
|
1570
|
+
MissingClientAuthError,
|
|
1249
1571
|
MissingRequiredArgumentError,
|
|
1250
1572
|
MissingSessionError,
|
|
1251
1573
|
MissingTransactionError,
|
|
1574
|
+
OrganizationValidationError,
|
|
1575
|
+
PasskeyChallengeError,
|
|
1576
|
+
PasskeyGetTokenError,
|
|
1577
|
+
PasskeyRegisterError,
|
|
1252
1578
|
ServerClient,
|
|
1253
1579
|
ServerMfaClient,
|
|
1580
|
+
ServerPasskeyClient,
|
|
1254
1581
|
StartLinkUserError,
|
|
1255
1582
|
StatefulStateStore,
|
|
1256
1583
|
StatelessStateStore,
|
|
1584
|
+
TokenExchangeError,
|
|
1257
1585
|
isMfaRequiredError
|
|
1258
1586
|
});
|
|
1259
1587
|
//# sourceMappingURL=index.cjs.map
|