Repository: auth0/auth0-spa-js
Pull request #385 (2020-03-19 16:36:12)

[SDK-1379] Export constructor
### Description

Add the option to use the `Auth0Client` constructor without having to use the factory.

The Existing API stays the same:

```javascript
// ES Modules
import createAuth0Client from '@auth0/auth0-spa-js';
// CommonJS
const createAuth0Client = require('@auth0/auth0-spa-js')
// Browser
window.createAuth0Client;
```

Now you can also access the [Auth0Client constructor](https://auth0.github.io/auth0-spa-js/classes/auth0client.html) directly:

```javascript
// ES Modules
import createAuth0Client, { Auth0Client } from '@auth0/auth0-spa-js';
// CommonJS
const { createAuth0Client, Auth0Client } = require('@auth0/auth0-spa-js')
// Browser
window.createAuth0Client;
window.Auth0Client;
```

If you access the `Auth0Client` constructor directly, it's up to you to check the user's session to automatically log the user in.

```javascript
import { Auth0Client } from '@auth0/auth0-spa-js';

const auth0 = new Auth0Client({ clientId, domain });
await auth0.getTokenSilently().catch(e => {
  if (e.error_message !== 'login_required') throw e;
});
```

### Testing

- [x] The integration tests check the browser behaviour
- [x] The Unit tests check the ES Module behaviour
- [x] Manually check the Common JS case
- [x] Test against [Vue sample](https://github.com/auth0-samples/auth0-vue-samples)
- [x] Test against [React sample](https://github.com/auth0-samples/auth0-react-samples/)
- [x] Test against [Angular sample](https://github.com/auth0-samples/auth0-angular-samples)
- [x] Test against [JavaScript sample](https://github.com/auth0-samples/auth0-javascript-samples)

### Checklist

- [x] I have added documentation for new/changed functionality in this PR or in auth0.com/docs
- [x] All active GitHub checks for tests, formatting, and security are passing
- [x] The correct base branch is being used, if not `master`



Patch:
diff --git a/README.md b/README.md
index c15ea6d..7ea0a89 100644
--- a/README.md
+++ b/README.md
@@ -67,6 +67,22 @@ createAuth0Client({
 }).then(auth0 => {
   //...
 });
+
+//or, you can just instantiate the client on it's own
+import { Auth0Client } from '@auth0/auth0-spa-js';
+const auth0 = new Auth0Client({
+  domain: '<AUTH0_DOMAIN>',
+  client_id: '<AUTH0_CLIENT_ID>',
+  redirect_uri: '<MY_CALLBACK_URL>'
+});
+//if you do this, you'll need to check the session yourself
+try {
+  await getTokenSilently();
+} catch (error) {
+  if (error.error !== 'login_required') {
+    throw error;
+  }
+}
 ```
 
 ### 1 - Login
diff --git a/cypress/integration/initialisation.js b/cypress/integration/initialisation.js
new file mode 100644
index 0000000..5d9dbb0
--- /dev/null
+++ b/cypress/integration/initialisation.js
@@ -0,0 +1,19 @@
+import { whenReady } from '../support/utils';
+
+describe('initialisation', function() {
+  beforeEach(cy.resetTests);
+
+  it('should expose a factory method and constructor', function(done) {
+    whenReady().then(win => {
+      assert.isFunction(
+        win.createAuth0Client,
+        'The createAuth0Client function should be declared on the window.'
+      );
+      assert.isFunction(
+        win.Auth0Client,
+        'The Auth0Client constructor should be declared on the window.'
+      );
+      done();
+    });
+  });
+});
diff --git a/package.json b/package.json
index cd0d185..35af15b 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,6 @@
   "version": "1.7.0-beta.4",
   "main": "dist/lib/auth0-spa-js.cjs.js",
   "types": "dist/typings/index.d.ts",
-  "browser": "dist/auth0-spa-js.production.js",
   "module": "dist/auth0-spa-js.production.esm.js",
   "scripts": {
     "dev": "rimraf dist && rollup -c --watch",
diff --git a/rollup.config.js b/rollup.config.js
index 2063fa0..b03b145 100644
--- a/rollup.config.js
+++ b/rollup.config.js
@@ -34,13 +34,16 @@ const getPlugins = shouldMinify => {
     sourcemaps()
   ];
 };
+const footer = `('Auth0Client' in this) && this.console && this.console.warn && this.console.warn('Auth0Client already declared on the global namespace');
+this && this.${EXPORT_NAME} && (this.Auth0Client = this.Auth0Client || this.${EXPORT_NAME}.Auth0Client);`;
 
 let bundles = [
   {
-    input: 'src/index.ts',
+    input: 'src/index.cjs.ts',
     output: {
       name: EXPORT_NAME,
       file: 'dist/auth0-spa-js.development.js',
+      footer,
       format: 'umd'
     },
     plugins: [
@@ -62,11 +65,12 @@ let bundles = [
 if (isProduction) {
   bundles = bundles.concat(
     {
-      input: 'src/index.ts',
+      input: 'src/index.cjs.ts',
       output: [
         {
           name: EXPORT_NAME,
-          file: pkg.browser,
+          file: 'dist/auth0-spa-js.production.js',
+          footer,
           format: 'umd'
         }
       ],
@@ -86,7 +90,7 @@ if (isProduction) {
       plugins: getPlugins(isProduction)
     },
     {
-      input: 'src/index.ts',
+      input: 'src/index.cjs.ts',
       output: [
         {
           name: EXPORT_NAME,
diff --git a/src/Auth0Client.ts b/src/Auth0Client.ts
index 84450cc..e0b1134 100644
--- a/src/Auth0Client.ts
+++ b/src/Auth0Client.ts
@@ -10,7 +10,8 @@ import {
   runIframe,
   sha256,
   bufferToBase64UrlEncoded,
-  oauthToken
+  oauthToken,
+  validateCrypto
 } from './utils';
 
 import { InMemoryCache, ICache, LocalStorageCache } from './cache';
@@ -79,6 +80,7 @@ export default class Auth0Client {
   cacheLocation: CacheLocation;
 
   constructor(private options: Auth0ClientOptions) {
+    validateCrypto();
     this.cacheLocation = options.cacheLocation || 'memory';
 
     if (!cacheFactory(this.cacheLocation)) {
diff --git a/src/index.cjs.ts b/src/index.cjs.ts
new file mode 100644
index 0000000..0efc19d
--- /dev/null
+++ b/src/index.cjs.ts
@@ -0,0 +1,6 @@
+import createAuth0Client, { Auth0Client } from './index';
+
+export default Object.assign(createAuth0Client, {
+  Auth0Client,
+  createAuth0Client
+});
diff --git a/src/index.ts b/src/index.ts
index 260eaf3..79eb86b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -7,25 +7,22 @@ import 'promise-polyfill/src/polyfill';
 import 'fast-text-encoding';
 import 'abortcontroller-polyfill/dist/abortcontroller-polyfill-only';
 
-import Auth0Client_ from './Auth0Client';
+import Auth0Client from './Auth0Client';
 import * as ClientStorage from './storage';
 import { Auth0ClientOptions } from './global';
 import { CACHE_LOCATION_MEMORY } from './constants';
 
 import './global';
-import { validateCrypto } from './utils';
 import { getUniqueScopes } from './utils';
 
 export * from './global';
 
 export default async function createAuth0Client(options: Auth0ClientOptions) {
-  validateCrypto();
-
   if (options.useRefreshTokens) {
     options.scope = getUniqueScopes(options.scope, 'offline_access');
   }
 
-  const auth0 = new Auth0Client_(options);
+  const auth0 = new Auth0Client(options);
 
   if (
     auth0.cacheLocation === CACHE_LOCATION_MEMORY &&
@@ -45,4 +42,4 @@ export default async function createAuth0Client(options: Auth0ClientOptions) {
   return auth0;
 }
 
-export type Auth0Client = Auth0Client_;
+export { Auth0Client };


Test patch:
diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts
index 79e161e..ac5da3b 100644
--- a/__tests__/index.test.ts
+++ b/__tests__/index.test.ts
@@ -5,9 +5,11 @@ jest.mock('../src/transaction-manager');
 jest.mock('../src/utils');
 
 import { CacheLocation } from '../src/global';
-import Auth0Client from '../src/Auth0Client';
 
-import createAuth0Client, { GetTokenSilentlyOptions } from '../src/index';
+import createAuth0Client, {
+  Auth0Client,
+  GetTokenSilentlyOptions
+} from '../src/index';
 
 import { AuthenticationError } from '../src/errors';
 import version from '../src/version';
@@ -351,9 +353,7 @@ describe('Auth0', () => {
     it('opens popup with correct popup, url and custom config', async () => {
       const { auth0, utils } = await setup();
       await auth0.loginWithPopup({}, { timeoutInSeconds: 1 });
-      expect(
-        utils.runPopup
-      ).toHaveBeenCalledWith(
+      expect(utils.runPopup).toHaveBeenCalledWith(
         `https://test.auth0.com/authorize?${TEST_QUERY_PARAMS}${TEST_TELEMETRY_QUERY_STRING}`,
         { timeoutInSeconds: 1 }
       );
@@ -369,9 +369,7 @@ describe('Auth0', () => {
         }
       );
 
-      expect(
-        utils.runPopup
-      ).toHaveBeenCalledWith(
+      expect(utils.runPopup).toHaveBeenCalledWith(
         `https://test.auth0.com/authorize?${TEST_QUERY_PARAMS}${TEST_TELEMETRY_QUERY_STRING}`,
         { timeoutInSeconds: 25 }
       );