Repository: auth0/auth0-spa-js (ts)

Issue:
getTokenSilently is slow even with cache hit.
### Description

Calling Auth0Client's getTokenSilently function takes about 50ms on my MacBook Pro even in cases where the token is retrieved from the cache. With 4x CPU throttling it's about 150ms which can seriously degrade the user experience.

### Reproduction

I'm calling the following function during data fetching.
`const token = await auth0Client.getTokenSilently();`
It takes about 50ms on my computer. When I rolled back to my auth0-spa-js dependency from 1.6.2 to 1.3.2 it went down to 0.2ms on average.
I think this performance issue is caused by the locking mechanism which was introduced in this PR:
https://github.com/auth0/auth0-spa-js/pull/238

### Proposed fix
I think the lock should be acquired only if there was no cache hit. That would eliminate the lag in almost every cases. If there's no cache hit, this 50ms won't be very significant.

This line should be moved to line no. 315.
https://github.com/auth0/auth0-spa-js/blob/48a88d708980213063f12c64c0da29a126f411f7/src/Auth0Client.ts#L304

### Environment

- **Version of this library used:** 1.6.2
- **Other relevant versions (language, server software, OS, browser):** Google Chrome, MacOS


Pull request description:
No longer acquires a browser lock if there was a hit on the cache
### Description

This fixes the behaviour reported in #338 where getting an access token is still slow even though there's a cached value, because of the browser lock.

The primary purpose of the browser lock is to serialize calls to the authorization and token endpoints. If those calls aren't being made (i.e. because it can use a cached value) then we don't need to acquire a lock.

### References

Fixes #338 

### Testing

Fixed an existing test to ensure lock was not being acquired.

- [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`


Patch:
diff --git a/src/Auth0Client.ts b/src/Auth0Client.ts
index 4ed5c21..1452177 100644
--- a/src/Auth0Client.ts
+++ b/src/Auth0Client.ts
@@ -332,8 +332,6 @@ export default class Auth0Client {
     options.scope = getUniqueScopes(this.DEFAULT_SCOPE, options.scope);
 
     try {
-      await lock.acquireLock(GET_TOKEN_SILENTLY_LOCK_KEY, 5000);
-
       if (!options.ignoreCache) {
         const cache = this.cache.get({
           scope: options.scope,
@@ -341,11 +339,12 @@ export default class Auth0Client {
         });
 
         if (cache) {
-          await lock.releaseLock(GET_TOKEN_SILENTLY_LOCK_KEY);
           return cache.access_token;
         }
       }
 
+      await lock.acquireLock(GET_TOKEN_SILENTLY_LOCK_KEY, 5000);
+
       const stateIn = encodeState(createRandomString());
       const nonceIn = createRandomString();
       const code_verifier = createRandomString();


Test patch:
diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts
index 0de29d8..d9f2ebd 100644
--- a/__tests__/index.test.ts
+++ b/__tests__/index.test.ts
@@ -1073,18 +1073,13 @@ describe('Auth0', () => {
 
         expect(token).toBe(TEST_ACCESS_TOKEN);
       });
-      it('acquires and releases lock when there is a cache', async () => {
+      it('does not acquire a lock when the cache is available', async () => {
         const { auth0, cache, lock } = await setup();
         cache.get.mockReturnValue({ access_token: TEST_ACCESS_TOKEN });
 
         await auth0.getTokenSilently();
-        expect(lock.acquireLockMock).toHaveBeenCalledWith(
-          GET_TOKEN_SILENTLY_LOCK_KEY,
-          5000
-        );
-        expect(lock.releaseLockMock).toHaveBeenCalledWith(
-          GET_TOKEN_SILENTLY_LOCK_KEY
-        );
+
+        expect(lock.acquireLockMock).not.toHaveBeenCalled();
       });
       it('continues method execution when there is no cache available', async () => {
         const { auth0, utils } = await setup();