Repository: auth0/auth0-spa-js
Pull request #32 (2019-06-21 22:25:00)

Reduce transaction cookie size
### Description

Reduces the minimum transaction cookie size from `~455b` to `~288b`. If you use a custom `appState`, your cookie size will be bigger.

This improves the total cookie size from further requests. Most web servers have a `8192b` limit for cookies and will fail the request if you have more than that. This leaves us with ~28 available failed transactions. Each successful redirect callback will clear its own transaction, so only failed/cancelled transactions count for this limit.

### References

https://help.heroku.com/TQ80D553/why-do-i-get-a-400-bad-request-response-when-i-have-large-cookies

### Testing

- [x] This change adds test coverage for new/changed/fixed functionality

luisrudge: > Does it make sense to add a regression test to verify the size of the cookie and that it doesn’t exceed the limit?

I don't think so. If we need to add another piece of information in the future, we just need to make sure we absolutely need it.

Patch:
diff --git a/src/Auth0Client.ts b/src/Auth0Client.ts
index 04c5586..06944cc 100644
--- a/src/Auth0Client.ts
+++ b/src/Auth0Client.ts
@@ -206,11 +206,9 @@ export default class Auth0Client {
       redirect_uri
     );
     const url = this._authorizeUrl(params);
-    this.transactionManager.create({
-      state: stateIn,
+    this.transactionManager.create(stateIn, {
       nonce: nonceIn,
       code_verifier,
-      code_challenge: code_challenge,
       appState,
       scope: params.scope,
       audience: params.audience || 'default'
diff --git a/src/transaction-manager.ts b/src/transaction-manager.ts
index 0459c8a..570ab7c 100644
--- a/src/transaction-manager.ts
+++ b/src/transaction-manager.ts
@@ -1,16 +1,14 @@
 import * as ClientStorage from './storage';
 
-const COOKIE_KEY = 'Auth0.spa-js.transactions.';
+const COOKIE_KEY = 'a0.spajs.txs.';
 const getTransactionKey = (state: string) => `${COOKIE_KEY}${state}`;
 
 interface Transaction {
-  state: string;
   nonce: string;
   scope: string;
   audience: string;
   appState?: any;
   code_verifier: string;
-  code_challenge: string;
 }
 interface Transactions {
   [key: string]: Transaction;
@@ -26,9 +24,9 @@ export default class TransactionManager {
         this.transactions[state] = ClientStorage.get<Transaction>(k);
       });
   }
-  public create(transaction: Transaction) {
-    this.transactions[transaction.state] = transaction;
-    ClientStorage.save(getTransactionKey(transaction.state), transaction, {
+  public create(state: string, transaction: Transaction) {
+    this.transactions[state] = transaction;
+    ClientStorage.save(getTransactionKey(state), transaction, {
       daysUntilExpire: 1
     });
   }


Test patch:
diff --git a/__tests__/index.test.ts b/__tests__/index.test.ts
index a39b93a..d781d06 100644
--- a/__tests__/index.test.ts
+++ b/__tests__/index.test.ts
@@ -328,15 +328,16 @@ describe('Auth0', () => {
       const { auth0, transactionManager } = await setup();
 
       await auth0.loginWithRedirect(REDIRECT_OPTIONS);
-      expect(transactionManager.create).toHaveBeenCalledWith({
-        appState: TEST_APP_STATE,
-        audience: 'default',
-        code_challenge: TEST_BASE64_ENCODED_STRING,
-        code_verifier: TEST_RANDOM_STRING,
-        nonce: TEST_RANDOM_STRING,
-        scope: TEST_SCOPES,
-        state: TEST_ENCODED_STATE
-      });
+      expect(transactionManager.create).toHaveBeenCalledWith(
+        TEST_ENCODED_STATE,
+        {
+          appState: TEST_APP_STATE,
+          audience: 'default',
+          code_verifier: TEST_RANDOM_STRING,
+          nonce: TEST_RANDOM_STRING,
+          scope: TEST_SCOPES
+        }
+      );
     });
     it('calls `window.location.assign` with the correct url', async () => {
       const { auth0 } = await setup();
diff --git a/__tests__/transaction-manager.test.ts b/__tests__/transaction-manager.test.ts
index e88da09..a40db18 100644
--- a/__tests__/transaction-manager.test.ts
+++ b/__tests__/transaction-manager.test.ts
@@ -1,12 +1,11 @@
 import TransactionManager from '../src/transaction-manager';
 
-const COOKIE_KEY = 'Auth0.spa-js.transactions.';
+const COOKIE_KEY = 'a0.spajs.txs.';
 
+const stateIn = 'stateIn';
 const transaction = {
-  state: 'stateIn',
   nonce: 'nonceIn',
   code_verifier: 'code_verifierIn',
-  code_challenge: 'code_challengeIn',
   appState: 'appStateIn',
   scope: 'scopeIn',
   audience: ' audienceIn'
@@ -22,8 +21,8 @@ describe('transaction manager', () => {
   describe('constructor', () => {
     it('loads transactions from localStorage (per key)', () => {
       getStorageMock().getAllKeys.mockReturnValue([
-        'Auth0.spa-js.transactions.key1',
-        'Auth0.spa-js.transactions.key2'
+        'a0.spajs.txs.key1',
+        'a0.spajs.txs.key2'
       ]);
       tm = new TransactionManager();
       expect(getStorageMock().getAllKeys).toHaveBeenCalled();
@@ -46,13 +45,13 @@ describe('transaction manager', () => {
       tm = new TransactionManager();
     });
     it('`create` creates the transaction', () => {
-      tm.create(transaction);
-      expect(tm.get(transaction.state)).toMatchObject(transaction);
+      tm.create(stateIn, transaction);
+      expect(tm.get(stateIn)).toMatchObject(transaction);
     });
     it('`create` saves the transaction in the storage', () => {
-      tm.create(transaction);
+      tm.create(stateIn, transaction);
       expect(getStorageMock().save).toHaveBeenCalledWith(
-        `Auth0.spa-js.transactions.${transaction.state}`,
+        `a0.spajs.txs.${stateIn}`,
         transaction,
         {
           daysUntilExpire: 1
@@ -60,22 +59,22 @@ describe('transaction manager', () => {
       );
     });
     it('`get` without a transaction should return undefined', () => {
-      expect(tm.get(transaction.state)).toBeUndefined();
+      expect(tm.get(stateIn)).toBeUndefined();
     });
     it('`get` with a transaction should return the transaction', () => {
-      tm.create(transaction);
-      expect(tm.get(transaction.state)).toMatchObject(transaction);
+      tm.create(stateIn, transaction);
+      expect(tm.get(stateIn)).toMatchObject(transaction);
     });
     it('`remove` removes the transaction', () => {
-      tm.create(transaction);
-      tm.remove(transaction.state);
-      expect(tm.get(transaction.state)).toBeUndefined();
+      tm.create(stateIn, transaction);
+      tm.remove(stateIn);
+      expect(tm.get(stateIn)).toBeUndefined();
     });
     it('`remove` removes transaction from storage', () => {
-      tm.create(transaction);
-      tm.remove(transaction.state);
+      tm.create(stateIn, transaction);
+      tm.remove(stateIn);
       expect(getStorageMock().remove).toHaveBeenLastCalledWith(
-        `Auth0.spa-js.transactions.${transaction.state}`
+        `a0.spajs.txs.${stateIn}`
       );
     });
   });