@auth0/auth0-spa-js 1.21.1 → 1.22.2

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.
@@ -17,6 +17,7 @@ export default class Auth0Client {
17
17
  private readonly isAuthenticatedCookieName;
18
18
  private readonly nowProvider;
19
19
  private readonly httpTimeoutMs;
20
+ private readonly useRefreshTokensFallback;
20
21
  cacheLocation: CacheLocation;
21
22
  private worker;
22
23
  constructor(options: Auth0ClientOptions);
@@ -24,7 +24,7 @@ export declare const CACHE_LOCATION_LOCAL_STORAGE = "localstorage";
24
24
  /**
25
25
  * @ignore
26
26
  */
27
- export declare const MISSING_REFRESH_TOKEN_ERROR_MESSAGE = "The web worker is missing the refresh token";
27
+ export declare const MISSING_REFRESH_TOKEN_ERROR_MESSAGE = "Missing Refresh Token";
28
28
  /**
29
29
  * @ignore
30
30
  */
@@ -48,3 +48,8 @@ export declare class MfaRequiredError extends GenericError {
48
48
  mfa_token: string;
49
49
  constructor(error: string, error_description: string, mfa_token: string);
50
50
  }
51
+ export declare class MissingRefreshTokenError extends GenericError {
52
+ audience: string;
53
+ scope: string;
54
+ constructor(audience: string, scope: string);
55
+ }
@@ -39,7 +39,7 @@ export interface BaseLoginOptions {
39
39
  *
40
40
  * This only affects the New Universal Login Experience.
41
41
  */
42
- screen_hint?: string;
42
+ screen_hint?: 'signup' | 'login' | string;
43
43
  /**
44
44
  * The user's email address or other identifier. When your app knows
45
45
  * which user is trying to authenticate, you can provide this parameter
@@ -138,6 +138,26 @@ export interface Auth0ClientOptions extends BaseLoginOptions {
138
138
  * **Note**: Use of refresh tokens must be enabled by an administrator on your Auth0 client application.
139
139
  */
140
140
  useRefreshTokens?: boolean;
141
+ /**
142
+ * If true, fallback to the technique of using a hidden iframe and the `authorization_code` grant with `prompt=none` when unable to use refresh tokens.
143
+ * The default setting is `true`.
144
+ *
145
+ * **Note**: There might be situations where doing silent auth with a Web Message response from an iframe is not possible,
146
+ * like when you're serving your application from the file system or a custom protocol (like in a Desktop or Native app).
147
+ * In situations like this you can disable the iframe fallback and handle the failed Refresh Grant and prompt the user to login interactively with `loginWithRedirect` or `loginWithPopup`."
148
+ *
149
+ * E.g. Using the `file:` protocol in an Electron application does not support that legacy technique.
150
+ *
151
+ * let token: string;
152
+ * try {
153
+ * token = await auth0.getTokenSilently();
154
+ * } catch (e) {
155
+ * if (e.error === 'missing_refresh_token' || e.error === 'invalid_grant') {
156
+ * auth0.loginWithRedirect();
157
+ * }
158
+ * }
159
+ */
160
+ useRefreshTokensFallback?: boolean;
141
161
  /**
142
162
  * A maximum number of seconds to wait before declaring background calls to /authorize as failed for timeout
143
163
  * Defaults to 60s.
@@ -154,6 +174,9 @@ export interface Auth0ClientOptions extends BaseLoginOptions {
154
174
  auth0Client?: {
155
175
  name: string;
156
176
  version: string;
177
+ env?: {
178
+ [key: string]: string;
179
+ };
157
180
  };
158
181
  /**
159
182
  * Sets an additional cookie with no SameSite attribute to support legacy browsers
@@ -13,3 +13,10 @@ export declare const sha256: (s: string) => Promise<any>;
13
13
  export declare const urlDecodeB64: (input: string) => string;
14
14
  export declare const bufferToBase64UrlEncoded: (input: number[] | Uint8Array) => string;
15
15
  export declare const validateCrypto: () => void;
16
+ /**
17
+ * Returns an empty string when value is falsy, or when it's value is included in the exclude argument.
18
+ * @param value The value to check
19
+ * @param exclude An array of values that should result in an empty string.
20
+ * @returns The value, or an empty string when falsy or included in the exclude argument.
21
+ */
22
+ export declare function valueOrEmptyString(value: string, exclude?: string[]): string;
@@ -1,2 +1,2 @@
1
- declare const _default: "1.21.1";
1
+ declare const _default: "1.22.2";
2
2
  export default _default;
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "name": "@auth0/auth0-spa-js",
4
4
  "description": "Auth0 SDK for Single Page Applications using Authorization Code Grant Flow with PKCE",
5
5
  "license": "MIT",
6
- "version": "1.21.1",
6
+ "version": "1.22.2",
7
7
  "main": "dist/lib/auth0-spa-js.cjs.js",
8
8
  "types": "dist/typings/index.d.ts",
9
9
  "module": "dist/auth0-spa-js.production.esm.js",
@@ -40,7 +40,6 @@
40
40
  "@typescript-eslint/parser": "^4.33.0",
41
41
  "browserstack-cypress-cli": "1.8.1",
42
42
  "cli-table": "^0.3.6",
43
- "codecov": "^3.8.3",
44
43
  "concurrently": "^6.4.0",
45
44
  "cypress": "7.2.0",
46
45
  "es-check": "^6.1.1",
@@ -71,7 +70,7 @@
71
70
  "rollup-plugin-web-worker-loader": "^1.6.1",
72
71
  "serve": "^12.0.1",
73
72
  "ts-jest": "^27.0.7",
74
- "tslib": "^2.3.1",
73
+ "tslib": "^2.4.0",
75
74
  "tslint": "^6.1.3",
76
75
  "tslint-config-security": "^1.16.0",
77
76
  "typedoc": "0.18.0",
@@ -81,9 +80,9 @@
81
80
  "dependencies": {
82
81
  "abortcontroller-polyfill": "^1.7.3",
83
82
  "browser-tabs-lock": "^1.2.15",
84
- "core-js": "^3.22.0",
83
+ "core-js": "^3.23.2",
85
84
  "es-cookie": "^1.3.2",
86
- "fast-text-encoding": "^1.0.3",
85
+ "fast-text-encoding": "^1.0.4",
87
86
  "promise-polyfill": "^8.2.3",
88
87
  "unfetch": "^4.2.0"
89
88
  },
@@ -27,7 +27,12 @@ import {
27
27
 
28
28
  import TransactionManager from './transaction-manager';
29
29
  import { verify as verifyIdToken } from './jwt';
30
- import { AuthenticationError, GenericError, TimeoutError } from './errors';
30
+ import {
31
+ AuthenticationError,
32
+ GenericError,
33
+ MissingRefreshTokenError,
34
+ TimeoutError
35
+ } from './errors';
31
36
 
32
37
  import {
33
38
  ClientStorage,
@@ -170,14 +175,17 @@ const getCustomInitialOptions = (
170
175
  auth0Client,
171
176
  authorizeTimeoutInSeconds,
172
177
  cacheLocation,
178
+ cache,
173
179
  client_id,
174
180
  domain,
175
181
  issuer,
176
182
  leeway,
177
183
  max_age,
184
+ nowProvider,
178
185
  redirect_uri,
179
186
  scope,
180
187
  useRefreshTokens,
188
+ useRefreshTokensFallback,
181
189
  useCookiesForTransactions,
182
190
  useFormData,
183
191
  ...customParams
@@ -202,6 +210,7 @@ export default class Auth0Client {
202
210
  private readonly isAuthenticatedCookieName: string;
203
211
  private readonly nowProvider: () => number | Promise<number>;
204
212
  private readonly httpTimeoutMs: number;
213
+ private readonly useRefreshTokensFallback: boolean;
205
214
 
206
215
  cacheLocation: CacheLocation;
207
216
  private worker: Worker;
@@ -299,6 +308,9 @@ export default class Auth0Client {
299
308
  }
300
309
 
301
310
  this.customOptions = getCustomInitialOptions(options);
311
+
312
+ this.useRefreshTokensFallback =
313
+ this.options.useRefreshTokensFallback !== false;
302
314
  }
303
315
 
304
316
  private _url(path: string) {
@@ -1183,7 +1195,14 @@ export default class Auth0Client {
1183
1195
  // and you don't have a refresh token in web worker memory
1184
1196
  // fallback to an iframe.
1185
1197
  if ((!cache || !cache.refresh_token) && !this.worker) {
1186
- return await this._getTokenFromIFrame(options);
1198
+ if (this.useRefreshTokensFallback) {
1199
+ return await this._getTokenFromIFrame(options);
1200
+ }
1201
+
1202
+ throw new MissingRefreshTokenError(
1203
+ options.audience || 'default',
1204
+ options.scope
1205
+ );
1187
1206
  }
1188
1207
 
1189
1208
  const redirect_uri =
@@ -1230,11 +1249,12 @@ export default class Auth0Client {
1230
1249
  if (
1231
1250
  // The web worker didn't have a refresh token in memory so
1232
1251
  // fallback to an iframe.
1233
- e.message === MISSING_REFRESH_TOKEN_ERROR_MESSAGE ||
1234
- // A refresh token was found, but is it no longer valid.
1235
- // Fallback to an iframe.
1236
- (e.message &&
1237
- e.message.indexOf(INVALID_REFRESH_TOKEN_ERROR_MESSAGE) > -1)
1252
+ (e.message.indexOf(MISSING_REFRESH_TOKEN_ERROR_MESSAGE) > -1 ||
1253
+ // A refresh token was found, but is it no longer valid.
1254
+ // Fallback to an iframe.
1255
+ (e.message &&
1256
+ e.message.indexOf(INVALID_REFRESH_TOKEN_ERROR_MESSAGE) > -1)) &&
1257
+ this.useRefreshTokensFallback
1238
1258
  ) {
1239
1259
  return await this._getTokenFromIFrame(options);
1240
1260
  }
package/src/constants.ts CHANGED
@@ -34,8 +34,7 @@ export const CACHE_LOCATION_LOCAL_STORAGE = 'localstorage';
34
34
  /**
35
35
  * @ignore
36
36
  */
37
- export const MISSING_REFRESH_TOKEN_ERROR_MESSAGE =
38
- 'The web worker is missing the refresh token';
37
+ export const MISSING_REFRESH_TOKEN_ERROR_MESSAGE = 'Missing Refresh Token';
39
38
 
40
39
  /**
41
40
  * @ignore
package/src/errors.ts CHANGED
@@ -3,6 +3,8 @@
3
3
  * https://github.com/gotwarlost/istanbul/issues/690
4
4
  */
5
5
 
6
+ import { valueOrEmptyString } from './utils';
7
+
6
8
  /**
7
9
  * Thrown when network requests to the Auth server fail.
8
10
  */
@@ -91,3 +93,16 @@ export class MfaRequiredError extends GenericError {
91
93
  Object.setPrototypeOf(this, MfaRequiredError.prototype);
92
94
  }
93
95
  }
96
+
97
+ export class MissingRefreshTokenError extends GenericError {
98
+ /* istanbul ignore next */
99
+ constructor(public audience: string, public scope: string) {
100
+ super(
101
+ 'missing_refresh_token',
102
+ `Missing Refresh Token (audience: '${valueOrEmptyString(audience, [
103
+ 'default'
104
+ ])}', scope: '${valueOrEmptyString(scope)}')`
105
+ );
106
+ Object.setPrototypeOf(this, MissingRefreshTokenError.prototype);
107
+ }
108
+ }
package/src/global.ts CHANGED
@@ -45,7 +45,7 @@ export interface BaseLoginOptions {
45
45
  *
46
46
  * This only affects the New Universal Login Experience.
47
47
  */
48
- screen_hint?: string;
48
+ screen_hint?: 'signup' | 'login' | string;
49
49
 
50
50
  /**
51
51
  * The user's email address or other identifier. When your app knows
@@ -158,6 +158,27 @@ export interface Auth0ClientOptions extends BaseLoginOptions {
158
158
  */
159
159
  useRefreshTokens?: boolean;
160
160
 
161
+ /**
162
+ * If true, fallback to the technique of using a hidden iframe and the `authorization_code` grant with `prompt=none` when unable to use refresh tokens.
163
+ * The default setting is `true`.
164
+ *
165
+ * **Note**: There might be situations where doing silent auth with a Web Message response from an iframe is not possible,
166
+ * like when you're serving your application from the file system or a custom protocol (like in a Desktop or Native app).
167
+ * In situations like this you can disable the iframe fallback and handle the failed Refresh Grant and prompt the user to login interactively with `loginWithRedirect` or `loginWithPopup`."
168
+ *
169
+ * E.g. Using the `file:` protocol in an Electron application does not support that legacy technique.
170
+ *
171
+ * let token: string;
172
+ * try {
173
+ * token = await auth0.getTokenSilently();
174
+ * } catch (e) {
175
+ * if (e.error === 'missing_refresh_token' || e.error === 'invalid_grant') {
176
+ * auth0.loginWithRedirect();
177
+ * }
178
+ * }
179
+ */
180
+ useRefreshTokensFallback?: boolean;
181
+
161
182
  /**
162
183
  * A maximum number of seconds to wait before declaring background calls to /authorize as failed for timeout
163
184
  * Defaults to 60s.
@@ -173,7 +194,11 @@ export interface Auth0ClientOptions extends BaseLoginOptions {
173
194
  * Internal property to send information about the client to the authorization server.
174
195
  * @internal
175
196
  */
176
- auth0Client?: { name: string; version: string };
197
+ auth0Client?: {
198
+ name: string;
199
+ version: string;
200
+ env?: { [key: string]: string };
201
+ };
177
202
 
178
203
  /**
179
204
  * Sets an additional cookie with no SameSite attribute to support legacy browsers
package/src/utils.ts CHANGED
@@ -241,3 +241,13 @@ export const validateCrypto = () => {
241
241
  `);
242
242
  }
243
243
  };
244
+
245
+ /**
246
+ * Returns an empty string when value is falsy, or when it's value is included in the exclude argument.
247
+ * @param value The value to check
248
+ * @param exclude An array of values that should result in an empty string.
249
+ * @returns The value, or an empty string when falsy or included in the exclude argument.
250
+ */
251
+ export function valueOrEmptyString(value: string, exclude: string[] = []) {
252
+ return value && !exclude.includes(value) ? value : '';
253
+ }
package/src/version.ts CHANGED
@@ -1 +1 @@
1
- export default '1.21.1';
1
+ export default '1.22.2';
@@ -1,4 +1,4 @@
1
- import { MISSING_REFRESH_TOKEN_ERROR_MESSAGE } from '../constants';
1
+ import { MissingRefreshTokenError } from '../errors';
2
2
  import { WorkerRefreshTokenMessage } from './worker.types';
3
3
 
4
4
  let refreshTokens: Record<string, string> = {};
@@ -50,7 +50,7 @@ const messageHandler = async ({
50
50
  const refreshToken = getRefreshToken(audience, scope);
51
51
 
52
52
  if (!refreshToken) {
53
- throw new Error(MISSING_REFRESH_TOKEN_ERROR_MESSAGE);
53
+ throw new MissingRefreshTokenError(audience, scope);
54
54
  }
55
55
 
56
56
  fetchOptions.body = useFormData