Repository: auth0/auth0-spa-js
Pull request #377 (2020-03-16 15:43:14)

[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 eb28568..11cb8f4 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 8b8e2e7..62ddacf 100644
--- a/package.json
+++ b/package.json
@@ -6,7 +6,6 @@
   "version": "1.6.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 9804745..3c220f1 100644
--- a/src/Auth0Client.ts
+++ b/src/Auth0Client.ts
@@ -10,7 +10,8 @@ import {
   runIframe,
   sha256,
   bufferToBase64UrlEncoded,
-  oauthToken
+  oauthToken,
+  validateCrypto
 } from './utils';
 
 import Cache from './cache';
@@ -35,6 +36,7 @@ export default class Auth0Client {
   private readonly DEFAULT_SCOPE = 'openid profile email';
 
   constructor(private options: Auth0ClientOptions) {
+    validateCrypto();
     this.cache = new Cache();
     this.transactionManager = new TransactionManager();
     this.domainUrl = `https://${this.options.domain}`;
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 0e6242d..d5211f0 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -11,11 +11,8 @@ import * as ClientStorage from './storage';
 
 //this is necessary to export the type definitions used in this file
 import './global';
-import { validateCrypto } from './utils';
 
 export default async function createAuth0Client(options: Auth0ClientOptions) {
-  validateCrypto();
-
   const auth0 = new Auth0Client(options);
 
   if (!ClientStorage.get('auth0.is.authenticated')) {
@@ -34,3 +31,5 @@ export default async function createAuth0Client(options: Auth0ClientOptions) {
   }
   return auth0;
 }
+
+export { Auth0Client };


Test patch:
diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts
index 5eec106..2d774cf 100644
--- a/__tests__/index.test.ts
+++ b/__tests__/index.test.ts
@@ -5,8 +5,7 @@ jest.mock('../src/cache');
 jest.mock('../src/transaction-manager');
 jest.mock('../src/utils');
 
-import Auth0Client from '../src/Auth0Client';
-import createAuth0Client from '../src/index';
+import createAuth0Client, { Auth0Client } from '../src/index';
 import { AuthenticationError } from '../src/errors';
 import version from '../src/version';
 const GET_TOKEN_SILENTLY_LOCK_KEY = 'auth0.lock.getTokenSilently';
@@ -315,7 +314,9 @@ 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 }
       );
@@ -324,7 +325,9 @@ describe('Auth0', () => {
     it('opens popup with correct popup, url and timeout from client options', async () => {
       const { auth0, utils } = await setup({ authorizeTimeoutInSeconds: 1 });
       await auth0.loginWithPopup({}, DEFAULT_POPUP_CONFIG_OPTIONS);
-      expect(utils.runPopup).toHaveBeenCalledWith(
+      expect(
+        utils.runPopup
+      ).toHaveBeenCalledWith(
         `https://test.auth0.com/authorize?${TEST_QUERY_PARAMS}${TEST_TELEMETRY_QUERY_STRING}`,
         { timeoutInSeconds: 1 }
       );