Repository: auth0/auth0-spa-js
Pull request #124 (2019-07-30 07:16:42)

Warning on unsecure origin
### Description

Actually, when `sha256` is called in a not secure context a `TypeError` bubble up because  `Crypto.subtle` is undefined.
I changed the function behavior: if `Crypto.subtle` is undefined then `sha256`  returns and log a meaningful warning message.
I also added a section to the FAQ with links related to Crypto API.

### References

- https://github.com/auth0/auth0-spa-js/issues/96
- https://github.com/auth0/auth0-spa-js/issues/82

### Testing

I added a unit test to the suite to cover both the return of `sha256`  in case `Crypto.subtle`  is undefined and the console message.
If needed I can eventually split the test in two different suites, one for the function and the other for the console warning.

- [X] This change adds test coverage for new/changed/fixed functionality

### 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`


gabrieledarrigo: > I love this! Thanks for the PR 🎉 Can we do this check inside the `createAuth0Client` method? I think it's easier if we warn the developer in the moment they start the SDK, instead of when using one of the methods. What do you think?

Ciao @luisrudge, yes it's a good idea!
So, before proceeding, I have few questions to point me in the right direction:
If the check inside `createAuth0Client` returns false, what should we do?
Warn the user and the return early without creating the client? This seems the best scenario for me.
For example:

```
export default async function createAuth0Client(options: Auth0ClientOptions) {
  if (typeof ) {
    console.warn('Auth0 sdk etc etc...');
    return;
  }

  const auth0 = new Auth0Client(options);
  // other function's code
}
```

Then, since we are checking inside `createAuth0Client` , do you prefer to test the same condition:

```
typeof window.crypto.subtle === 'undefined'
```

Or we can test something more suitable to the case?
I was wondering that, if we keep the same condition to test, we can move the check inside a utility function ad use it inside  `createAuth0Client`.
Let me know!

luisrudge: Yeah, I think you can do a console.error and just bail out. No need to continue because nothing will work. I think we can start simple for now, let's go with the typeof check. If we need to to more check in the future, we can abstract into its own function.
gabrieledarrigo: That's fine!
I'll try to change the code this evening!
gabrieledarrigo: Ok, I've rebased to master and moved the check inside `createAuth0Client`, adding the required tests inside `index.js` (I covered both the branch where the client can be undefined or not, let me know if you prefer to cover only the `undefined` case).
Waiting for your opinion!
luisrudge: @gabrieledarrigo I made a few changes to the PR, but it looks pretty good. Thanks for the PR 🎉 
gabrieledarrigo: That's totally fine, glad to help 😃 !
ps: I fixed the failing test.
luisrudge: OPS! forgot to change that ahahaha. Thanks!

Patch:
diff --git a/FAQ.md b/FAQ.md
index 0a776ba..0c12d72 100644
--- a/FAQ.md
+++ b/FAQ.md
@@ -62,3 +62,20 @@ With this change, an immediate refresh after login works as expected.
 Note that even though the workaround doesn't cause any weird side effects in browers, you should ideally remove it after the bug has been fixed in Firefox.
 
 For more context see this [issue](https://github.com/auth0-samples/auth0-react-samples/issues/145).
+
+## Why do I get `auth0-spa-js must run on a secure origin`?
+
+Internally, the SDK uses [Web Cryptography API](https://developer.mozilla.org/en-US/docs/Web/API/Web_Crypto_API) to create [SHA-256 digest](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/digest).
+
+According to the spec ([via Github issues](https://github.com/w3c/webcrypto/issues/28)), Web Cryptography API requires a secure origin, so that accessing `Crypto.subtle` in a not secure context return undefined.
+
+In most browsers, secure origins are origins that match at least one of the following (scheme, host, port) patterns: 
+
+```
+(https, *, *)
+(wss, *, *)
+(*, localhost, *)
+(*, 127/8, *)
+(*, ::1/128, *)
+(file, *, —)
+```
diff --git a/src/index.ts b/src/index.ts
index 19574e8..ec8fd20 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -6,6 +6,15 @@ import * as ClientStorage from './storage';
 import './global';
 
 export default async function createAuth0Client(options: Auth0ClientOptions) {
+  if (typeof window.crypto.subtle === 'undefined') {
+    console.error(`
+      auth0-spa-js must run on a secure origin.
+      See https://github.com/auth0/auth0-spa-js/blob/master/FAQ.md#why-do-i-get-error-invalid-state-in-firefox-when-refreshing-the-page-immediately-after-a-login 
+      for more information.
+    `);
+    return;
+  }
+
   const auth0 = new Auth0Client(options);
 
   if (!ClientStorage.get('auth0.is.authenticated')) {


Test patch:
diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts
index 323e68e..dd41699 100644
--- a/__tests__/index.test.ts
+++ b/__tests__/index.test.ts
@@ -86,6 +86,36 @@ describe('Auth0', () => {
   beforeEach(() => {
     jest.resetAllMocks();
     window.location.assign = jest.fn();
+    (<any>global).crypto = {
+      subtle: {
+        digest: () => ''
+      }
+    };
+  });
+  describe('createAuth0Client()', () => {
+    it('should create an Auth0 client', async () => {
+      const auth0 = await createAuth0Client({
+        domain: TEST_DOMAIN,
+        client_id: TEST_CLIENT_ID
+      });
+      expect(auth0).toBeInstanceOf(Auth0Client);
+    });
+    it('should return, logging a warning if crypto.digest is undefined', async () => {
+      (<any>global).crypto = {};
+      (<any>window).console = {
+        error: jest.fn()
+      };
+      const auth0 = await createAuth0Client({
+        domain: TEST_DOMAIN,
+        client_id: TEST_CLIENT_ID
+      });
+      expect(auth0).toBeUndefined();
+      expect(window.console.error).toHaveBeenCalledWith(`
+      auth0-spa-js must run on a secure origin.
+      See https://github.com/auth0/auth0-spa-js/blob/master/FAQ.md#why-do-i-get-error-invalid-state-in-firefox-when-refreshing-the-page-immediately-after-a-login 
+      for more information.
+    `);
+    });
   });
   describe('loginWithPopup()', () => {
     it('opens popup', async () => {