@auth0/auth0-server-js 1.1.0 → 1.3.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.js CHANGED
@@ -34,26 +34,33 @@ var MissingSessionError = class extends Error {
34
34
  this.name = "MissingSessionError";
35
35
  }
36
36
  };
37
+ var InvalidConfigurationError = class extends Error {
38
+ code = "invalid_configuration_error";
39
+ constructor(message) {
40
+ super(message);
41
+ this.name = "InvalidConfigurationError";
42
+ }
43
+ };
37
44
 
38
45
  // src/state/utils.ts
39
- function updateStateData(audience, stateDate, tokenEndpointResponse) {
40
- if (stateDate) {
41
- const isNewTokenSet = !stateDate.tokenSets.some(
46
+ var createUpdatedTokenSet = (audience, response) => ({
47
+ audience,
48
+ accessToken: response.accessToken,
49
+ scope: response.scope,
50
+ expiresAt: response.expiresAt
51
+ });
52
+ function updateStateData(audience, stateData, tokenEndpointResponse) {
53
+ if (stateData) {
54
+ const isNewTokenSet = !stateData.tokenSets.some(
42
55
  (tokenSet) => tokenSet.audience === audience && tokenSet.scope === tokenEndpointResponse.scope
43
56
  );
44
- const createUpdatedTokenSet = (response) => ({
45
- audience,
46
- accessToken: response.accessToken,
47
- scope: response.scope,
48
- expiresAt: Math.floor(Date.now() / 1e3) + Number(response.expiresAt)
49
- });
50
- const tokenSets = isNewTokenSet ? [...stateDate.tokenSets, createUpdatedTokenSet(tokenEndpointResponse)] : stateDate.tokenSets.map(
51
- (tokenSet) => tokenSet.audience === audience && tokenSet.scope === tokenEndpointResponse.scope ? createUpdatedTokenSet(tokenEndpointResponse) : tokenSet
57
+ const tokenSets = isNewTokenSet ? [...stateData.tokenSets, createUpdatedTokenSet(audience, tokenEndpointResponse)] : stateData.tokenSets.map(
58
+ (tokenSet) => tokenSet.audience === audience && tokenSet.scope === tokenEndpointResponse.scope ? createUpdatedTokenSet(audience, tokenEndpointResponse) : tokenSet
52
59
  );
53
60
  return {
54
- ...stateDate,
55
- idToken: tokenEndpointResponse.idToken,
56
- refreshToken: tokenEndpointResponse.refreshToken ?? stateDate.refreshToken,
61
+ ...stateData,
62
+ idToken: tokenEndpointResponse.idToken ?? stateData.idToken,
63
+ refreshToken: tokenEndpointResponse.refreshToken ?? stateData.refreshToken,
57
64
  tokenSets
58
65
  };
59
66
  } else {
@@ -63,12 +70,7 @@ function updateStateData(audience, stateDate, tokenEndpointResponse) {
63
70
  idToken: tokenEndpointResponse.idToken,
64
71
  refreshToken: tokenEndpointResponse.refreshToken,
65
72
  tokenSets: [
66
- {
67
- audience,
68
- accessToken: tokenEndpointResponse.accessToken,
69
- scope: tokenEndpointResponse.scope,
70
- expiresAt: tokenEndpointResponse.expiresAt
71
- }
73
+ createUpdatedTokenSet(audience, tokenEndpointResponse)
72
74
  ],
73
75
  internal: {
74
76
  sid: user?.sid,
@@ -77,9 +79,9 @@ function updateStateData(audience, stateDate, tokenEndpointResponse) {
77
79
  };
78
80
  }
79
81
  }
80
- function updateStateDataForConnectionTokenSet(options, stateDate, tokenEndpointResponse) {
81
- stateDate.connectionTokenSets = stateDate.connectionTokenSets || [];
82
- const isNewTokenSet = !stateDate.connectionTokenSets.some(
82
+ function updateStateDataForConnectionTokenSet(options, stateData, tokenEndpointResponse) {
83
+ stateData.connectionTokenSets = stateData.connectionTokenSets || [];
84
+ const isNewTokenSet = !stateData.connectionTokenSets.some(
83
85
  (tokenSet) => tokenSet.connection === options.connection && (!options.loginHint || tokenSet.loginHint === options.loginHint)
84
86
  );
85
87
  const connectionTokenSet = {
@@ -89,11 +91,11 @@ function updateStateDataForConnectionTokenSet(options, stateDate, tokenEndpointR
89
91
  scope: tokenEndpointResponse.scope,
90
92
  expiresAt: tokenEndpointResponse.expiresAt
91
93
  };
92
- const connectionTokenSets = isNewTokenSet ? [...stateDate.connectionTokenSets, connectionTokenSet] : stateDate.connectionTokenSets.map(
94
+ const connectionTokenSets = isNewTokenSet ? [...stateData.connectionTokenSets, connectionTokenSet] : stateData.connectionTokenSets.map(
93
95
  (tokenSet) => tokenSet.connection === options.connection && (!options.loginHint || tokenSet.loginHint === options.loginHint) ? connectionTokenSet : tokenSet
94
96
  );
95
97
  return {
96
- ...stateDate,
98
+ ...stateData,
97
99
  connectionTokenSets
98
100
  };
99
101
  }
@@ -118,7 +120,30 @@ var compareScopes = (scopes, requiredScopes) => {
118
120
  return requiredScopesArray.every((scope) => scopesSet.has(scope));
119
121
  };
120
122
 
123
+ // src/telemetry.ts
124
+ function getTelemetryConfig(config) {
125
+ if (config?.enabled === false) {
126
+ return config;
127
+ }
128
+ return {
129
+ enabled: true,
130
+ name: config?.name ?? "@auth0/auth0-server-js",
131
+ version: config?.version ?? "1.3.0"
132
+ };
133
+ }
134
+
121
135
  // src/server-client.ts
136
+ var DEFAULT_SCOPES = "openid profile email offline_access";
137
+ function ensureOpenIdScope(scope) {
138
+ if (!scope) {
139
+ return DEFAULT_SCOPES;
140
+ }
141
+ const scopes = scope.split(" ");
142
+ if (!scopes.includes("openid")) {
143
+ scopes.unshift("openid");
144
+ }
145
+ return scopes.join(" ");
146
+ }
122
147
  var ServerClient = class {
123
148
  #options;
124
149
  #transactionStore;
@@ -152,7 +177,8 @@ var ServerClient = class {
152
177
  clientAssertionSigningAlg: this.#options.clientAssertionSigningAlg,
153
178
  authorizationParams: this.#options.authorizationParams,
154
179
  customFetch: this.#options.customFetch,
155
- useMtls: this.#options.useMtls
180
+ useMtls: this.#options.useMtls,
181
+ telemetry: getTelemetryConfig(this.#options.telemetry)
156
182
  });
157
183
  }
158
184
  /**
@@ -169,11 +195,13 @@ var ServerClient = class {
169
195
  if (!redirectUri) {
170
196
  throw new MissingRequiredArgumentError("authorizationParams.redirect_uri");
171
197
  }
198
+ const scope = ensureOpenIdScope(options?.authorizationParams?.scope ?? this.#options.authorizationParams?.scope);
172
199
  const { codeVerifier, authorizationUrl } = await this.authClient.buildAuthorizationUrl({
173
200
  pushedAuthorizationRequests: options?.pushedAuthorizationRequests,
174
201
  authorizationParams: {
175
202
  ...options?.authorizationParams,
176
- redirect_uri: redirectUri
203
+ redirect_uri: redirectUri,
204
+ scope
177
205
  }
178
206
  });
179
207
  const transactionState = {
@@ -323,10 +351,14 @@ var ServerClient = class {
323
351
  * @returns A promise resolving to an object, containing the authorizationDetails (when RAR was used).
324
352
  */
325
353
  async loginBackchannel(options, storeOptions) {
354
+ const scope = ensureOpenIdScope(options.authorizationParams?.scope ?? this.#options.authorizationParams?.scope);
326
355
  const tokenEndpointResponse = await this.authClient.backchannelAuthentication({
327
356
  bindingMessage: options.bindingMessage,
328
357
  loginHint: options.loginHint,
329
- authorizationParams: options.authorizationParams
358
+ authorizationParams: {
359
+ ...options.authorizationParams,
360
+ scope
361
+ }
330
362
  });
331
363
  const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
332
364
  const stateData = updateStateData(
@@ -363,16 +395,24 @@ var ServerClient = class {
363
395
  /**
364
396
  * 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.
365
397
  * Also updates the store when a new token was retrieved from Auth0.
398
+ *
399
+ * @param options Optional options for requesting specific audience/scope.
366
400
  * @param storeOptions Optional options used to pass to the Transaction and State Store.
367
401
  *
368
402
  * @throws {TokenByRefreshTokenError} If the refresh token was not found or there was an issue requesting the access token.
369
403
  *
370
404
  * @returns The Token Set, containing the access token, as well as additional information.
371
405
  */
372
- async getAccessToken(storeOptions) {
373
- const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
374
- const audience = this.#options.authorizationParams?.audience ?? "default";
375
- const scope = this.#options.authorizationParams?.scope;
406
+ async getAccessToken(tokenOptionsOrStoreOptions, storeOptions) {
407
+ const hasTokenOptions = (
408
+ // If second arg exists, first arg must be GetAccessTokenOptions
409
+ storeOptions !== void 0 || // OR if first arg has audience/scope properties
410
+ tokenOptionsOrStoreOptions && typeof tokenOptionsOrStoreOptions === "object" && ("audience" in tokenOptionsOrStoreOptions || "scope" in tokenOptionsOrStoreOptions)
411
+ );
412
+ const [resolvedOptions, resolvedStoreOptions] = hasTokenOptions ? [tokenOptionsOrStoreOptions, storeOptions] : [void 0, tokenOptionsOrStoreOptions];
413
+ const stateData = await this.#stateStore.get(this.#stateStoreIdentifier, resolvedStoreOptions);
414
+ const audience = resolvedOptions?.audience ?? this.#options.authorizationParams?.audience ?? "default";
415
+ const scope = resolvedOptions?.scope ?? this.#options.authorizationParams?.scope;
376
416
  const tokenSet = stateData?.tokenSets.find(
377
417
  (tokenSet2) => tokenSet2.audience === audience && (!scope || compareScopes(tokenSet2.scope, scope))
378
418
  );
@@ -384,12 +424,14 @@ var ServerClient = class {
384
424
  "The access token has expired and a refresh token was not provided. The user needs to re-authenticate."
385
425
  );
386
426
  }
387
- const tokenEndpointResponse = await this.authClient.getTokenByRefreshToken({
388
- refreshToken: stateData.refreshToken
389
- });
390
- const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, storeOptions);
427
+ const tokenByRefreshTokenOptions = {
428
+ refreshToken: stateData.refreshToken,
429
+ ...hasTokenOptions && { audience, scope }
430
+ };
431
+ const tokenEndpointResponse = await this.authClient.getTokenByRefreshToken(tokenByRefreshTokenOptions);
432
+ const existingStateData = await this.#stateStore.get(this.#stateStoreIdentifier, resolvedStoreOptions);
391
433
  const updatedStateData = updateStateData(audience, existingStateData, tokenEndpointResponse);
392
- await this.#stateStore.set(this.#stateStoreIdentifier, updatedStateData, false, storeOptions);
434
+ await this.#stateStore.set(this.#stateStoreIdentifier, updatedStateData, false, resolvedStoreOptions);
393
435
  return {
394
436
  accessToken: tokenEndpointResponse.accessToken,
395
437
  scope: tokenEndpointResponse.scope,
@@ -468,13 +510,16 @@ var ServerClient = class {
468
510
  };
469
511
 
470
512
  // src/encryption/index.ts
471
- import { EncryptJWT, jwtDecrypt } from "jose";
513
+ import { EncryptJWT, jwtDecrypt, errors } from "jose";
472
514
  var ENC = "A256CBC-HS512";
473
515
  var ALG = "dir";
474
516
  var DIGEST = "SHA-256";
475
517
  var BIT_LENGTH = 512;
476
518
  var HKDF_INFO = "derived cookie encryption secret";
477
519
  var encoder;
520
+ function isDecryptionError(e) {
521
+ return e instanceof errors.JWEDecryptionFailed || e instanceof errors.JWEInvalid;
522
+ }
478
523
  async function deriveEncryptionSecret(secret, salt, kid) {
479
524
  encoder ||= new TextEncoder();
480
525
  const key = await crypto.subtle.importKey("raw", encoder.encode(secret), "HKDF", false, ["deriveBits"]);
@@ -491,12 +536,23 @@ async function deriveEncryptionSecret(secret, salt, kid) {
491
536
  )
492
537
  );
493
538
  }
494
- async function encrypt(payload, secret, salt, expiration) {
539
+ async function _encrypt(payload, secret, salt, expiration) {
495
540
  const kid = crypto.randomUUID();
496
541
  const encryptionSecret = await deriveEncryptionSecret(secret, salt, kid);
497
542
  return await new EncryptJWT(payload).setProtectedHeader({ enc: ENC, alg: ALG, kid }).setExpirationTime(expiration).encrypt(encryptionSecret);
498
543
  }
499
- async function decrypt(value, secret, salt) {
544
+ async function encrypt(payload, secret, salt, expiration) {
545
+ if (typeof secret === "string") {
546
+ return await _encrypt(payload, secret, salt, expiration);
547
+ } else {
548
+ const [newSecret] = secret;
549
+ if (!newSecret) {
550
+ throw new InvalidConfigurationError("At least one secret must be provided");
551
+ }
552
+ return await _encrypt(payload, newSecret, salt, expiration);
553
+ }
554
+ }
555
+ async function _decrypt(value, secret, salt) {
500
556
  const res = await jwtDecrypt(
501
557
  value,
502
558
  async (protectedHeader) => {
@@ -509,6 +565,35 @@ async function decrypt(value, secret, salt) {
509
565
  );
510
566
  return res.payload;
511
567
  }
568
+ async function decrypt(value, secret, salt) {
569
+ if (typeof secret === "string") {
570
+ return await _decrypt(value, secret, salt);
571
+ } else {
572
+ const [newSecret, ...oldSecrets] = secret;
573
+ if (!newSecret) {
574
+ throw new InvalidConfigurationError("At least one secret must be provided");
575
+ }
576
+ let firstError;
577
+ try {
578
+ return await _decrypt(value, newSecret, salt);
579
+ } catch (err) {
580
+ firstError = err;
581
+ if (!isDecryptionError(err)) {
582
+ throw firstError;
583
+ }
584
+ for (const oldSecret of oldSecrets) {
585
+ try {
586
+ return await _decrypt(value, oldSecret, salt);
587
+ } catch (fallbackErr) {
588
+ if (!isDecryptionError(fallbackErr)) {
589
+ throw fallbackErr;
590
+ }
591
+ }
592
+ }
593
+ throw firstError;
594
+ }
595
+ }
596
+ }
512
597
 
513
598
  // src/store/abstract-store.ts
514
599
  var AbstractStore = class {
@@ -520,7 +605,14 @@ var AbstractStore = class {
520
605
  return await encrypt(stateData, this.options.secret, identifier, expiration);
521
606
  }
522
607
  async decrypt(identifier, encryptedStateData) {
523
- return await decrypt(encryptedStateData, this.options.secret, identifier);
608
+ try {
609
+ return await decrypt(encryptedStateData, this.options.secret, identifier);
610
+ } catch (e) {
611
+ if (isDecryptionError(e)) {
612
+ return;
613
+ }
614
+ throw e;
615
+ }
524
616
  }
525
617
  };
526
618
 
@@ -612,13 +704,9 @@ var StatefulStateStore = class extends AbstractSessionStore {
612
704
  }
613
705
  sessionId ??= generateId();
614
706
  const maxAge = this.calculateMaxAge(stateData.internal.createdAt);
615
- const cookieOpts = {
616
- httpOnly: true,
617
- sameSite: this.#cookieOptions?.sameSite ?? "lax",
618
- path: "/",
619
- secure: this.#cookieOptions?.secure,
707
+ const cookieOpts = this.#getCookieOptions({
620
708
  maxAge
621
- };
709
+ });
622
710
  const expiration = Date.now() / 1e3 + maxAge;
623
711
  const encryptedStateData = await this.encrypt(
624
712
  identifier,
@@ -635,7 +723,7 @@ var StatefulStateStore = class extends AbstractSessionStore {
635
723
  if (sessionId) {
636
724
  const stateData = await this.#store.get(sessionId);
637
725
  if (!stateData) {
638
- this.#cookieHandler.deleteCookie(identifier, options);
726
+ this.#cookieHandler.deleteCookie(identifier, options, this.#getCookieOptions());
639
727
  }
640
728
  return stateData;
641
729
  }
@@ -645,18 +733,29 @@ var StatefulStateStore = class extends AbstractSessionStore {
645
733
  if (sessionId) {
646
734
  await this.#store.delete(sessionId);
647
735
  }
648
- this.#cookieHandler.deleteCookie(identifier, options);
736
+ this.#cookieHandler.deleteCookie(identifier, options, this.#getCookieOptions());
649
737
  }
650
738
  async getSessionId(identifier, options) {
651
739
  const cookieValue = this.#cookieHandler.getCookie(identifier, options);
652
740
  if (cookieValue) {
653
741
  const sessionCookie = await this.decrypt(identifier, cookieValue);
654
- return sessionCookie.id;
742
+ if (sessionCookie) {
743
+ return sessionCookie.id;
744
+ }
655
745
  }
656
746
  }
657
747
  deleteByLogoutToken(claims, options) {
658
748
  return this.#store.deleteByLogoutToken(claims, options);
659
749
  }
750
+ #getCookieOptions(partial) {
751
+ return {
752
+ httpOnly: true,
753
+ sameSite: this.#cookieOptions?.sameSite ?? "lax",
754
+ path: this.#cookieOptions?.path ?? "/",
755
+ secure: this.#cookieOptions?.secure ?? true,
756
+ ...partial
757
+ };
758
+ }
660
759
  };
661
760
 
662
761
  // src/store/stateless-state-store.ts
@@ -670,13 +769,9 @@ var StatelessStateStore = class extends AbstractSessionStore {
670
769
  }
671
770
  async set(identifier, stateData, removeIfExists, options) {
672
771
  const maxAge = this.calculateMaxAge(stateData.internal.createdAt);
673
- const cookieOpts = {
674
- httpOnly: true,
675
- sameSite: this.#cookieOptions?.sameSite ?? "lax",
676
- path: "/",
677
- secure: this.#cookieOptions?.secure ?? true,
772
+ const cookieOpts = this.#getCookieOptions({
678
773
  maxAge
679
- };
774
+ });
680
775
  const expiration = Math.floor(Date.now() / 1e3 + maxAge);
681
776
  const encryptedStateData = await this.encrypt(identifier, stateData, expiration);
682
777
  const chunkSize = 3072;
@@ -691,7 +786,7 @@ var StatelessStateStore = class extends AbstractSessionStore {
691
786
  const existingCookieKeys = this.getCookieKeys(identifier, options);
692
787
  const cookieKeysToRemove = existingCookieKeys.filter((key) => !chunks.some((chunk) => chunk.name === key));
693
788
  cookieKeysToRemove.forEach((key) => {
694
- this.#cookieHandler.deleteCookie(key, options);
789
+ this.#cookieHandler.deleteCookie(key, options, cookieOpts);
695
790
  });
696
791
  }
697
792
  async get(identifier, options) {
@@ -707,7 +802,7 @@ var StatelessStateStore = class extends AbstractSessionStore {
707
802
  async delete(identifier, options) {
708
803
  const cookieKeys = this.getCookieKeys(identifier, options);
709
804
  for (const key of cookieKeys) {
710
- this.#cookieHandler.deleteCookie(key, options);
805
+ this.#cookieHandler.deleteCookie(key, options, this.#getCookieOptions());
711
806
  }
712
807
  }
713
808
  deleteByLogoutToken() {
@@ -718,12 +813,22 @@ var StatelessStateStore = class extends AbstractSessionStore {
718
813
  getCookieKeys(identifier, options) {
719
814
  return Object.keys(this.#cookieHandler.getCookies(options)).filter((key) => key.startsWith(identifier));
720
815
  }
816
+ #getCookieOptions(partial) {
817
+ return {
818
+ httpOnly: true,
819
+ sameSite: this.#cookieOptions?.sameSite ?? "lax",
820
+ path: this.#cookieOptions?.path ?? "/",
821
+ secure: this.#cookieOptions?.secure ?? true,
822
+ ...partial
823
+ };
824
+ }
721
825
  };
722
826
  export {
723
827
  AbstractStateStore,
724
828
  AbstractTransactionStore,
725
829
  BackchannelLogoutError,
726
830
  CookieTransactionStore,
831
+ InvalidConfigurationError,
727
832
  MissingRequiredArgumentError,
728
833
  MissingSessionError,
729
834
  MissingTransactionError,