@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.js
CHANGED
|
@@ -122,10 +122,24 @@ function updateStateDataForConnectionTokenSet(options, stateData, tokenEndpointR
|
|
|
122
122
|
import {
|
|
123
123
|
TokenForConnectionError,
|
|
124
124
|
AuthClient,
|
|
125
|
+
PasswordlessStartError,
|
|
126
|
+
PasswordlessVerifyError,
|
|
125
127
|
TokenByRefreshTokenError
|
|
126
128
|
} from "@auth0/auth0-auth-js";
|
|
127
129
|
|
|
128
130
|
// src/utils.ts
|
|
131
|
+
var DEFAULT_SCOPES = "openid profile email offline_access";
|
|
132
|
+
var ensureOpenIdScope = (scope) => {
|
|
133
|
+
const normalizedScope = scope?.trim();
|
|
134
|
+
if (!normalizedScope) {
|
|
135
|
+
return DEFAULT_SCOPES;
|
|
136
|
+
}
|
|
137
|
+
const scopes = normalizedScope.split(/\s+/);
|
|
138
|
+
if (!scopes.includes("openid")) {
|
|
139
|
+
scopes.unshift("openid");
|
|
140
|
+
}
|
|
141
|
+
return scopes.join(" ");
|
|
142
|
+
};
|
|
129
143
|
var compareScopes = (scopes, requiredScopes) => {
|
|
130
144
|
if (scopes === requiredScopes) {
|
|
131
145
|
return true;
|
|
@@ -149,7 +163,7 @@ function getTelemetryConfig(config) {
|
|
|
149
163
|
return {
|
|
150
164
|
enabled: true,
|
|
151
165
|
name: config?.name ?? "@auth0/auth0-server-js",
|
|
152
|
-
version: config?.version ?? "1.
|
|
166
|
+
version: config?.version ?? "1.7.0"
|
|
153
167
|
};
|
|
154
168
|
}
|
|
155
169
|
|
|
@@ -233,8 +247,99 @@ var ServerMfaClient = class {
|
|
|
233
247
|
}
|
|
234
248
|
};
|
|
235
249
|
|
|
250
|
+
// src/passkey/server-passkey-client.ts
|
|
251
|
+
var ServerPasskeyClient = class {
|
|
252
|
+
#options;
|
|
253
|
+
/**
|
|
254
|
+
* @internal
|
|
255
|
+
*/
|
|
256
|
+
constructor(options) {
|
|
257
|
+
this.#options = options;
|
|
258
|
+
}
|
|
259
|
+
/**
|
|
260
|
+
* Requests a passkey signup challenge for a new user.
|
|
261
|
+
*
|
|
262
|
+
* Returns the `authSession` and the WebAuthn credential creation options
|
|
263
|
+
* (`authnParamsPublicKey`). The application must return these to the browser,
|
|
264
|
+
* pass `authnParamsPublicKey` to `navigator.credentials.create()`, and then
|
|
265
|
+
* call `getToken()` with the resulting credential to complete signup.
|
|
266
|
+
*
|
|
267
|
+
* This method does not create a session; no state is persisted.
|
|
268
|
+
*
|
|
269
|
+
* @param options User profile data and optional realm/organization.
|
|
270
|
+
* @param storeOptions Optional options used to resolve the domain (resolver mode).
|
|
271
|
+
*
|
|
272
|
+
* @throws {PasskeyRegisterError} If there was an issue requesting the signup challenge.
|
|
273
|
+
*
|
|
274
|
+
* @returns A promise resolving to the signup challenge.
|
|
275
|
+
*/
|
|
276
|
+
async register(options, storeOptions) {
|
|
277
|
+
const domain = await this.#options.resolveDomain(storeOptions);
|
|
278
|
+
const authClient = this.#options.getAuthClient(domain);
|
|
279
|
+
return authClient.passkey.register(options);
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Requests a passkey login challenge for an existing user.
|
|
283
|
+
*
|
|
284
|
+
* Returns the `authSession` and the WebAuthn credential request options
|
|
285
|
+
* (`authnParamsPublicKey`). The application must return these to the browser,
|
|
286
|
+
* pass `authnParamsPublicKey` to `navigator.credentials.get()`, and then
|
|
287
|
+
* call `getToken()` with the resulting credential to complete login.
|
|
288
|
+
*
|
|
289
|
+
* This method does not create a session; no state is persisted.
|
|
290
|
+
*
|
|
291
|
+
* @param options Optional realm/organization configuration.
|
|
292
|
+
* @param storeOptions Optional options used to resolve the domain (resolver mode).
|
|
293
|
+
*
|
|
294
|
+
* @throws {PasskeyChallengeError} If there was an issue requesting the login challenge.
|
|
295
|
+
*
|
|
296
|
+
* @returns A promise resolving to the login challenge.
|
|
297
|
+
*/
|
|
298
|
+
async challenge(options, storeOptions) {
|
|
299
|
+
const domain = await this.#options.resolveDomain(storeOptions);
|
|
300
|
+
const authClient = this.#options.getAuthClient(domain);
|
|
301
|
+
return authClient.passkey.challenge(options);
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* Completes a passkey authentication flow (signup or login) by exchanging the
|
|
305
|
+
* WebAuthn credential for tokens, and persists the resulting session.
|
|
306
|
+
*
|
|
307
|
+
* Call this after obtaining a credential from `navigator.credentials.create()`
|
|
308
|
+
* (signup) or `navigator.credentials.get()` (login), passing the `authSession`
|
|
309
|
+
* returned by `register()` / `challenge()` together with the serialized credential.
|
|
310
|
+
*
|
|
311
|
+
* In resolver (multi-tenant) mode, pass the same `storeOptions` you passed to
|
|
312
|
+
* `register()` / `challenge()` so the token exchange resolves the same tenant
|
|
313
|
+
* that issued the `authSession`; otherwise the exchange will fail.
|
|
314
|
+
*
|
|
315
|
+
* @param options The auth session, serialized credential, and optional realm/scope/audience/organization.
|
|
316
|
+
* @param storeOptions Optional options used to pass to the State Store (and to resolve the domain in resolver mode).
|
|
317
|
+
*
|
|
318
|
+
* @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.
|
|
319
|
+
* @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.
|
|
320
|
+
*
|
|
321
|
+
* @returns A promise resolving to an object containing the authorizationDetails (when RAR was used).
|
|
322
|
+
*/
|
|
323
|
+
async getToken(options, storeOptions) {
|
|
324
|
+
const scope = ensureOpenIdScope(options.scope ?? this.#options.defaultScope);
|
|
325
|
+
const audience = options.audience ?? this.#options.defaultAudience;
|
|
326
|
+
const domain = await this.#options.resolveDomain(storeOptions);
|
|
327
|
+
const authClient = this.#options.getAuthClient(domain);
|
|
328
|
+
const tokenEndpointResponse = await authClient.passkey.getTokenByPasskey({
|
|
329
|
+
...options,
|
|
330
|
+
scope,
|
|
331
|
+
audience
|
|
332
|
+
});
|
|
333
|
+
const existingStateData = await this.#options.stateStore.get(this.#options.stateStoreIdentifier, storeOptions);
|
|
334
|
+
const stateData = updateStateData(audience ?? "default", existingStateData, tokenEndpointResponse, { domain });
|
|
335
|
+
await this.#options.stateStore.set(this.#options.stateStoreIdentifier, stateData, true, storeOptions);
|
|
336
|
+
return {
|
|
337
|
+
authorizationDetails: tokenEndpointResponse.authorizationDetails
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
};
|
|
341
|
+
|
|
236
342
|
// src/server-client.ts
|
|
237
|
-
var DEFAULT_SCOPES = "openid profile email offline_access";
|
|
238
343
|
var normalizeDomain = (value) => {
|
|
239
344
|
const trimmed = value.trim();
|
|
240
345
|
const parsed = trimmed.startsWith("http") ? new URL(trimmed) : new URL(`https://${trimmed}`);
|
|
@@ -248,16 +353,6 @@ var decodeIssuer = (token) => {
|
|
|
248
353
|
return void 0;
|
|
249
354
|
}
|
|
250
355
|
};
|
|
251
|
-
var ensureOpenIdScope = (scope) => {
|
|
252
|
-
if (!scope) {
|
|
253
|
-
return DEFAULT_SCOPES;
|
|
254
|
-
}
|
|
255
|
-
const scopes = scope.split(" ");
|
|
256
|
-
if (!scopes.includes("openid")) {
|
|
257
|
-
scopes.unshift("openid");
|
|
258
|
-
}
|
|
259
|
-
return scopes.join(" ");
|
|
260
|
-
};
|
|
261
356
|
var ServerClient = class {
|
|
262
357
|
#options;
|
|
263
358
|
#transactionStore;
|
|
@@ -268,6 +363,7 @@ var ServerClient = class {
|
|
|
268
363
|
#staticDomain;
|
|
269
364
|
#authClient;
|
|
270
365
|
#mfaClient;
|
|
366
|
+
#passkeyClient;
|
|
271
367
|
/**
|
|
272
368
|
* The underlying `authClient` instance that can be used to interact with the Auth0 Authentication API.
|
|
273
369
|
* Generally, you should prefer to use the higher-level methods exposed on the `ServerClient` instance.
|
|
@@ -302,6 +398,19 @@ var ServerClient = class {
|
|
|
302
398
|
}
|
|
303
399
|
return this.#mfaClient;
|
|
304
400
|
}
|
|
401
|
+
/**
|
|
402
|
+
* The passkey client for signing up and logging in users with WebAuthn credentials.
|
|
403
|
+
*
|
|
404
|
+
* Provides `register()` and `challenge()` to request signup/login challenges, and
|
|
405
|
+
* `getToken()` to exchange the resulting credential for tokens and persist the session.
|
|
406
|
+
*
|
|
407
|
+
* Unlike `mfa`, this property is available in both static and resolver (multi-tenant)
|
|
408
|
+
* domain modes. In resolver mode, pass the same `storeOptions` to `register()`/`challenge()`
|
|
409
|
+
* and `getToken()` so the credential is exchanged against the tenant that issued it.
|
|
410
|
+
*/
|
|
411
|
+
get passkey() {
|
|
412
|
+
return this.#passkeyClient;
|
|
413
|
+
}
|
|
305
414
|
constructor(options) {
|
|
306
415
|
this.#options = options;
|
|
307
416
|
this.#stateStoreIdentifier = this.#options.stateIdentifier || "__a0_session";
|
|
@@ -343,6 +452,14 @@ var ServerClient = class {
|
|
|
343
452
|
defaultAudience: this.#options.authorizationParams?.audience ?? "default"
|
|
344
453
|
});
|
|
345
454
|
}
|
|
455
|
+
this.#passkeyClient = new ServerPasskeyClient({
|
|
456
|
+
resolveDomain: (storeOptions) => this.#resolveDomain(storeOptions),
|
|
457
|
+
getAuthClient: (domain) => this.#getAuthClient(domain),
|
|
458
|
+
stateStore: this.#stateStore,
|
|
459
|
+
stateStoreIdentifier: this.#stateStoreIdentifier,
|
|
460
|
+
defaultScope: this.#options.authorizationParams?.scope,
|
|
461
|
+
defaultAudience: this.#options.authorizationParams?.audience
|
|
462
|
+
});
|
|
346
463
|
}
|
|
347
464
|
async #resolveDomain(storeOptions) {
|
|
348
465
|
if (typeof this.#options.domain === "function") {
|
|
@@ -447,6 +564,7 @@ var ServerClient = class {
|
|
|
447
564
|
const domain = transactionData.domain ?? await this.#resolveDomain(storeOptions);
|
|
448
565
|
const authClient = this.#getAuthClient(domain);
|
|
449
566
|
const tokenEndpointResponse = await authClient.getTokenByCode(url, {
|
|
567
|
+
// TransactionData.codeVerifier is optional only to accommodate magic-link transactions.
|
|
450
568
|
codeVerifier: transactionData.codeVerifier
|
|
451
569
|
});
|
|
452
570
|
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
@@ -610,6 +728,178 @@ var ServerClient = class {
|
|
|
610
728
|
authorizationDetails: tokenEndpointResponse.authorizationDetails
|
|
611
729
|
};
|
|
612
730
|
}
|
|
731
|
+
/**
|
|
732
|
+
* Starts a passwordless flow by sending a one-time code (OTP) or a magic link.
|
|
733
|
+
*
|
|
734
|
+
* Discriminated on `connection` (and, for email, `send`) to mirror the
|
|
735
|
+
* `@auth0/nextjs-auth0` `passwordless.start()` surface:
|
|
736
|
+
* - `{ connection: 'email' }` / `{ connection: 'email', send: 'code' }` — email OTP
|
|
737
|
+
* - `{ connection: 'email', send: 'link', redirectUri }` — email magic link
|
|
738
|
+
* - `{ connection: 'sms' }` — SMS OTP
|
|
739
|
+
*
|
|
740
|
+
* OTP modes are a stateless passthrough to the Authentication API (no session, no transaction);
|
|
741
|
+
* complete them with {@link ServerClient#completePasswordless}.
|
|
742
|
+
*
|
|
743
|
+
* Magic-link mode is stateful: the SDK generates an opaque anti-forgery `state`, sends the link
|
|
744
|
+
* with the OAuth parameters embedded (`redirect_uri`, `response_type=code`, `scope`, `state`),
|
|
745
|
+
* and persists a transaction carrying that `state`. NO PKCE challenge is registered, so the
|
|
746
|
+
* transaction holds no `codeVerifier`. Complete it with
|
|
747
|
+
* {@link ServerClient#completePasswordlessMagicLink}. Requires the tenant setting
|
|
748
|
+
* `allow_magiclink_verify_without_session: true` for server-side completion.
|
|
749
|
+
*
|
|
750
|
+
* @param options Discriminated start options.
|
|
751
|
+
* @param storeOptions Optional options passed to the resolver / stores.
|
|
752
|
+
*
|
|
753
|
+
* @throws {PasswordlessStartError} If the request fails, or if a magic link is requested without a `redirectUri`.
|
|
754
|
+
*
|
|
755
|
+
* @example
|
|
756
|
+
* // Email OTP
|
|
757
|
+
* await serverClient.startPasswordless({ connection: 'email', email: 'user@example.com' });
|
|
758
|
+
* // SMS OTP
|
|
759
|
+
* await serverClient.startPasswordless({ connection: 'sms', phoneNumber: '+14155550100' });
|
|
760
|
+
* // Email magic link
|
|
761
|
+
* await serverClient.startPasswordless({
|
|
762
|
+
* connection: 'email',
|
|
763
|
+
* email: 'user@example.com',
|
|
764
|
+
* send: 'link',
|
|
765
|
+
* redirectUri: 'https://app.example.com/auth/callback',
|
|
766
|
+
* });
|
|
767
|
+
*/
|
|
768
|
+
async startPasswordless(options, storeOptions) {
|
|
769
|
+
const domain = await this.#resolveDomain(storeOptions);
|
|
770
|
+
const authClient = this.#getAuthClient(domain);
|
|
771
|
+
if (options.connection === "sms") {
|
|
772
|
+
await authClient.passwordless.sendSms({
|
|
773
|
+
phoneNumber: options.phoneNumber,
|
|
774
|
+
language: options.language
|
|
775
|
+
});
|
|
776
|
+
return;
|
|
777
|
+
}
|
|
778
|
+
if (options.send !== "link") {
|
|
779
|
+
await authClient.passwordless.sendEmail({
|
|
780
|
+
email: options.email,
|
|
781
|
+
send: "code",
|
|
782
|
+
language: options.language
|
|
783
|
+
});
|
|
784
|
+
return;
|
|
785
|
+
}
|
|
786
|
+
if (!options.redirectUri || typeof options.redirectUri !== "string") {
|
|
787
|
+
throw new PasswordlessStartError("redirectUri is required to start a passwordless magic-link login.");
|
|
788
|
+
}
|
|
789
|
+
const state = crypto.randomUUID();
|
|
790
|
+
const scope = ensureOpenIdScope(options.scope ?? this.#options.authorizationParams?.scope);
|
|
791
|
+
const audience = options.audience ?? this.#options.authorizationParams?.audience;
|
|
792
|
+
await authClient.passwordless.sendEmail({
|
|
793
|
+
email: options.email,
|
|
794
|
+
send: "link",
|
|
795
|
+
language: options.language,
|
|
796
|
+
authParams: {
|
|
797
|
+
...options.authParams,
|
|
798
|
+
redirect_uri: options.redirectUri,
|
|
799
|
+
response_type: "code",
|
|
800
|
+
scope,
|
|
801
|
+
...audience ? { audience } : {},
|
|
802
|
+
state
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
const transactionState = {
|
|
806
|
+
audience,
|
|
807
|
+
domain,
|
|
808
|
+
state
|
|
809
|
+
};
|
|
810
|
+
await this.#transactionStore.set(this.#transactionStoreIdentifier, transactionState, false, storeOptions);
|
|
811
|
+
}
|
|
812
|
+
/**
|
|
813
|
+
* Completes a passwordless OTP login and persists the resulting session.
|
|
814
|
+
*
|
|
815
|
+
* Discriminated on `connection` to mirror the `@auth0/nextjs-auth0` `passwordless.verify()`
|
|
816
|
+
* surface. Non-redirect flow: no PKCE and no transaction store (mirrors
|
|
817
|
+
* {@link ServerClient#loginBackchannel}). The `openid` scope is always ensured by this layer.
|
|
818
|
+
*
|
|
819
|
+
* Note: the state store is read-then-written; if your deployment performs concurrent
|
|
820
|
+
* logins for the same session identifier, use a state store with atomic/serializable
|
|
821
|
+
* writes to avoid last-write-wins races.
|
|
822
|
+
*
|
|
823
|
+
* @param options Discriminated completion options (`connection`, identifier, `verificationCode`).
|
|
824
|
+
* @param storeOptions Optional options passed to the resolver / stores.
|
|
825
|
+
*
|
|
826
|
+
* @throws {PasswordlessVerifyError} If the code is invalid, expired, or rate-limited. When the
|
|
827
|
+
* connection requires MFA, the server responds with `mfa_required`; narrow the thrown error
|
|
828
|
+
* with `isMfaRequiredError(error)` to read `cause.mfa_token`.
|
|
829
|
+
*
|
|
830
|
+
* @returns A promise resolving to the authorizationDetails (when RAR was used).
|
|
831
|
+
*/
|
|
832
|
+
async completePasswordless(options, storeOptions) {
|
|
833
|
+
const scope = ensureOpenIdScope(options.authorizationParams?.scope ?? this.#options.authorizationParams?.scope);
|
|
834
|
+
const audience = options.authorizationParams?.audience ?? this.#options.authorizationParams?.audience;
|
|
835
|
+
const domain = await this.#resolveDomain(storeOptions);
|
|
836
|
+
const authClient = this.#getAuthClient(domain);
|
|
837
|
+
const tokenEndpointResponse = options.connection === "sms" ? await authClient.getTokenByPasswordlessSms({
|
|
838
|
+
phoneNumber: options.phoneNumber,
|
|
839
|
+
code: options.verificationCode,
|
|
840
|
+
audience,
|
|
841
|
+
scope
|
|
842
|
+
}) : await authClient.getTokenByPasswordlessEmail({
|
|
843
|
+
email: options.email,
|
|
844
|
+
code: options.verificationCode,
|
|
845
|
+
audience,
|
|
846
|
+
scope
|
|
847
|
+
});
|
|
848
|
+
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
849
|
+
const stateData = updateStateData(
|
|
850
|
+
this.#options.authorizationParams?.audience ?? "default",
|
|
851
|
+
existingStateData,
|
|
852
|
+
tokenEndpointResponse,
|
|
853
|
+
{ domain }
|
|
854
|
+
);
|
|
855
|
+
await this.#stateStore.set(this.#stateStoreIdentifier, stateData, true, storeOptions);
|
|
856
|
+
return {
|
|
857
|
+
authorizationDetails: tokenEndpointResponse.authorizationDetails
|
|
858
|
+
};
|
|
859
|
+
}
|
|
860
|
+
/**
|
|
861
|
+
* Completes a passwordless magic-link login and persists the resulting session.
|
|
862
|
+
*
|
|
863
|
+
* Loads the transaction persisted by {@link ServerClient#startPasswordless} (magic-link mode), validates the
|
|
864
|
+
* `state` returned on the callback URL against the stored `state` (anti-forgery binding), exchanges
|
|
865
|
+
* the authorization code WITHOUT PKCE, writes the session, and deletes the transaction. The existing
|
|
866
|
+
* interactive login path ({@link ServerClient#completeInteractiveLogin}) is not used.
|
|
867
|
+
*
|
|
868
|
+
* @param url The callback URL containing the authorization `code` and `state`.
|
|
869
|
+
* @param storeOptions Optional options passed to the resolver / stores.
|
|
870
|
+
*
|
|
871
|
+
* @throws {MissingTransactionError} If no magic-link transaction was found.
|
|
872
|
+
* @throws {PasswordlessVerifyError} If the returned `state` is missing or does not match.
|
|
873
|
+
* @throws {TokenByCodeError} If the token exchange fails.
|
|
874
|
+
*
|
|
875
|
+
* @returns A promise resolving to the authorizationDetails (when RAR was used).
|
|
876
|
+
*
|
|
877
|
+
* @example
|
|
878
|
+
* const result = await serverClient.completePasswordlessMagicLink(callbackUrl, storeOptions);
|
|
879
|
+
*/
|
|
880
|
+
async completePasswordlessMagicLink(url, storeOptions) {
|
|
881
|
+
const transactionData = await this.#transactionStore.get(this.#transactionStoreIdentifier, storeOptions);
|
|
882
|
+
if (!transactionData) {
|
|
883
|
+
throw new MissingTransactionError();
|
|
884
|
+
}
|
|
885
|
+
const expectedState = typeof transactionData.state === "string" ? transactionData.state : void 0;
|
|
886
|
+
const returnedState = url.searchParams.get("state");
|
|
887
|
+
if (!returnedState || !expectedState || returnedState !== expectedState) {
|
|
888
|
+
throw new PasswordlessVerifyError("State mismatch on magic-link callback");
|
|
889
|
+
}
|
|
890
|
+
const domain = transactionData.domain ?? await this.#resolveDomain(storeOptions);
|
|
891
|
+
const authClient = this.#getAuthClient(domain);
|
|
892
|
+
const tokenEndpointResponse = await authClient.getTokenByMagicLinkCode(url, { expectedState });
|
|
893
|
+
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
|
|
894
|
+
const stateData = updateStateData(transactionData.audience ?? "default", existingStateData, tokenEndpointResponse, {
|
|
895
|
+
domain
|
|
896
|
+
});
|
|
897
|
+
await this.#stateStore.set(this.#stateStoreIdentifier, stateData, true, storeOptions);
|
|
898
|
+
await this.#transactionStore.delete(this.#transactionStoreIdentifier, storeOptions);
|
|
899
|
+
return {
|
|
900
|
+
authorizationDetails: tokenEndpointResponse.authorizationDetails
|
|
901
|
+
};
|
|
902
|
+
}
|
|
613
903
|
/**
|
|
614
904
|
* Retrieves the user from the store, or undefined if no user found.
|
|
615
905
|
* @param storeOptions Optional options used to pass to the Transaction and State Store.
|
|
@@ -649,16 +939,29 @@ var ServerClient = class {
|
|
|
649
939
|
/**
|
|
650
940
|
* 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.
|
|
651
941
|
* Also updates the store when a new token was retrieved from Auth0.
|
|
942
|
+
*
|
|
943
|
+
* When `options.audience` and/or `options.scope` are provided, the SDK uses the session's refresh token to
|
|
944
|
+
* request an access token for that audience/scope (Multi-Resource Refresh Tokens). Tokens are cached per
|
|
945
|
+
* audience and scope combination.
|
|
946
|
+
*
|
|
947
|
+
* @param options Optional options for requesting a specific audience/scope.
|
|
652
948
|
* @param storeOptions Optional options used to pass to the Transaction and State Store.
|
|
653
949
|
*
|
|
654
950
|
* @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`.
|
|
655
951
|
*
|
|
656
952
|
* @returns The Token Set, containing the access token, as well as additional information.
|
|
657
953
|
*/
|
|
658
|
-
async getAccessToken(storeOptions) {
|
|
659
|
-
const
|
|
660
|
-
|
|
661
|
-
|
|
954
|
+
async getAccessToken(tokenOptionsOrStoreOptions, storeOptions) {
|
|
955
|
+
const hasTokenOptions = (
|
|
956
|
+
// If second arg exists, first arg must be GetAccessTokenOptions
|
|
957
|
+
storeOptions !== void 0 || // OR if first arg has audience/scope properties
|
|
958
|
+
!!tokenOptionsOrStoreOptions && typeof tokenOptionsOrStoreOptions === "object" && ("audience" in tokenOptionsOrStoreOptions || "scope" in tokenOptionsOrStoreOptions)
|
|
959
|
+
);
|
|
960
|
+
const [resolvedOptions, resolvedStoreOptions] = hasTokenOptions ? [tokenOptionsOrStoreOptions, storeOptions] : [void 0, tokenOptionsOrStoreOptions];
|
|
961
|
+
const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, resolvedStoreOptions);
|
|
962
|
+
const requestedAudience = resolvedOptions?.audience ?? this.#options.authorizationParams?.audience;
|
|
963
|
+
const audience = requestedAudience ?? "default";
|
|
964
|
+
const scope = resolvedOptions?.scope ?? this.#options.authorizationParams?.scope;
|
|
662
965
|
const sessionDomain = stateData ? this.#getSessionDomain(stateData) : this.#staticDomain;
|
|
663
966
|
if (this.#isResolverMode()) {
|
|
664
967
|
if (!stateData) {
|
|
@@ -667,7 +970,7 @@ var ServerClient = class {
|
|
|
667
970
|
if (!sessionDomain) {
|
|
668
971
|
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
669
972
|
}
|
|
670
|
-
const resolvedDomain = await this.#resolveDomain(
|
|
973
|
+
const resolvedDomain = await this.#resolveDomain(resolvedStoreOptions);
|
|
671
974
|
if (sessionDomain !== resolvedDomain) {
|
|
672
975
|
throw new MissingSessionError("Session domain does not match the current domain.");
|
|
673
976
|
}
|
|
@@ -684,14 +987,21 @@ var ServerClient = class {
|
|
|
684
987
|
);
|
|
685
988
|
}
|
|
686
989
|
const domainForSession = sessionDomain;
|
|
687
|
-
const
|
|
688
|
-
refreshToken: stateData.refreshToken
|
|
689
|
-
|
|
690
|
-
|
|
990
|
+
const tokenByRefreshTokenOptions = {
|
|
991
|
+
refreshToken: stateData.refreshToken,
|
|
992
|
+
// Only forward audience/scope to Auth0 when token options were explicitly supplied, and
|
|
993
|
+
// never send the synthetic 'default' cache-key audience as a real request parameter.
|
|
994
|
+
...hasTokenOptions && {
|
|
995
|
+
...requestedAudience && { audience: requestedAudience },
|
|
996
|
+
...scope && { scope }
|
|
997
|
+
}
|
|
998
|
+
};
|
|
999
|
+
const tokenEndpointResponse = await this.#getAuthClient(domainForSession).getTokenByRefreshToken(tokenByRefreshTokenOptions);
|
|
1000
|
+
const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, resolvedStoreOptions);
|
|
691
1001
|
const updatedStateData = updateStateData(audience, existingStateData, tokenEndpointResponse, {
|
|
692
1002
|
domain: domainForSession
|
|
693
1003
|
});
|
|
694
|
-
await this.#stateStore.set(this.#stateStoreIdentifier, updatedStateData, false,
|
|
1004
|
+
await this.#stateStore.set(this.#stateStoreIdentifier, updatedStateData, false, resolvedStoreOptions);
|
|
695
1005
|
return {
|
|
696
1006
|
accessToken: tokenEndpointResponse.accessToken,
|
|
697
1007
|
scope: tokenEndpointResponse.scope,
|
|
@@ -998,6 +1308,9 @@ var AbstractTransactionStore = class extends AbstractStore {
|
|
|
998
1308
|
}
|
|
999
1309
|
};
|
|
1000
1310
|
|
|
1311
|
+
// src/index.ts
|
|
1312
|
+
import { TokenExchangeError, MissingClientAuthError } from "@auth0/auth0-auth-js";
|
|
1313
|
+
|
|
1001
1314
|
// src/store/cookie-transaction-store.ts
|
|
1002
1315
|
var CookieTransactionStore = class extends AbstractTransactionStore {
|
|
1003
1316
|
#cookieHandler;
|
|
@@ -1200,6 +1513,14 @@ import {
|
|
|
1200
1513
|
MfaVerifyError,
|
|
1201
1514
|
isMfaRequiredError
|
|
1202
1515
|
} from "@auth0/auth0-auth-js";
|
|
1516
|
+
|
|
1517
|
+
// src/passkey/index.ts
|
|
1518
|
+
import {
|
|
1519
|
+
PasskeyRegisterError,
|
|
1520
|
+
PasskeyChallengeError,
|
|
1521
|
+
PasskeyGetTokenError,
|
|
1522
|
+
OrganizationValidationError
|
|
1523
|
+
} from "@auth0/auth0-auth-js";
|
|
1203
1524
|
export {
|
|
1204
1525
|
AbstractStateStore,
|
|
1205
1526
|
AbstractTransactionStore,
|
|
@@ -1211,14 +1532,21 @@ export {
|
|
|
1211
1532
|
MfaEnrollmentError,
|
|
1212
1533
|
MfaListAuthenticatorsError,
|
|
1213
1534
|
MfaVerifyError,
|
|
1535
|
+
MissingClientAuthError,
|
|
1214
1536
|
MissingRequiredArgumentError,
|
|
1215
1537
|
MissingSessionError,
|
|
1216
1538
|
MissingTransactionError,
|
|
1539
|
+
OrganizationValidationError,
|
|
1540
|
+
PasskeyChallengeError,
|
|
1541
|
+
PasskeyGetTokenError,
|
|
1542
|
+
PasskeyRegisterError,
|
|
1217
1543
|
ServerClient,
|
|
1218
1544
|
ServerMfaClient,
|
|
1545
|
+
ServerPasskeyClient,
|
|
1219
1546
|
StartLinkUserError,
|
|
1220
1547
|
StatefulStateStore,
|
|
1221
1548
|
StatelessStateStore,
|
|
1549
|
+
TokenExchangeError,
|
|
1222
1550
|
isMfaRequiredError
|
|
1223
1551
|
};
|
|
1224
1552
|
//# sourceMappingURL=index.js.map
|