Repository: auth0/auth0-spa-js
Pull request #717 (2021-03-05 11:17:05)

feat(loginWithRedirect): add redirectMethod option
### Description

As per [this closed issue](https://github.com/auth0/auth0-spa-js/issues/183) where a PR contribution was suggested, we have a similar requirement (i.e. not to allow users to use 'browser back' to go back to a page where `loginWithRedirect` was called - and want to use `window.location.replace` to achieve this).

### References

[Add support for redirect with window.replace instead of window.assign #183](https://github.com/auth0/auth0-spa-js/issues/183)

### Testing

I have tested locally using `npm link` passing the new param `redirectMethod` to `loginWithRedirect`.
I have ensured that this param is not passed to the `buildAuthorizeUrl` method as we don't need it to be present as a search param on the Authorize URL.

I have added a new unit test for the change and verified that the existing unit tests pass without alteration indicating this is a non-breaking change.

- [x] This change adds test coverage for new/changed/fixed functionality

### Checklist

- [ ] 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
- [ ] The correct base branch is being used, if not `master`


stevehobbsdev: Thanks for raising @slaywell, let us have a deep-dive into the PR this week, we'll come back to you shortly.
stevehobbsdev: All looks good @slaywell, thanks. Just attempting to fix build issues before we merge and release 🎉 

Patch:
diff --git a/.circleci/config.yml b/.circleci/config.yml
index f433b22..8beedee 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -125,8 +125,8 @@ jobs:
       - run:
           name: Install SPA JS
           command: |
-            pwd
             yarn add "../spa-js"
+            touch src/auth0.js
             echo -e "$AUTH0_CONTENT" > src/auth0.js;
             echo -e "$IMPORT_STATEMENT"|cat - src/main.js > /tmp/out && mv /tmp/out src/main.js;
           working_directory: my-app
diff --git a/src/Auth0Client.ts b/src/Auth0Client.ts
index b40ae78..77352b9 100644
--- a/src/Auth0Client.ts
+++ b/src/Auth0Client.ts
@@ -480,8 +480,9 @@ export default class Auth0Client {
    * @param options
    */
   public async loginWithRedirect(options: RedirectLoginOptions = {}) {
-    const url = await this.buildAuthorizeUrl(options);
-    window.location.assign(url);
+    const { redirectMethod, ...urlOptions } = options;
+    const url = await this.buildAuthorizeUrl(urlOptions);
+    window.location[redirectMethod || 'assign'](url);
   }
 
   /**
diff --git a/src/global.ts b/src/global.ts
index 66a4c3b..867670c 100644
--- a/src/global.ts
+++ b/src/global.ts
@@ -233,6 +233,10 @@ export interface RedirectLoginOptions extends BaseLoginOptions {
    * Used to add to the URL fragment before redirecting
    */
   fragment?: string;
+  /**
+   * Used to select the window.location method used to redirect
+   */
+  redirectMethod?: 'replace' | 'assign';
 }
 
 export interface RedirectLoginResult {


Test patch:
diff --git a/__tests__/Auth0Client/helpers.ts b/__tests__/Auth0Client/helpers.ts
index 8446bac..b5023be 100644
--- a/__tests__/Auth0Client/helpers.ts
+++ b/__tests__/Auth0Client/helpers.ts
@@ -137,7 +137,8 @@ export const loginWithRedirectFn = (mockWindow, mockFetch) => {
       customCallbackUrl
     } = processDefaultLoginWithRedirectOptions(testConfig);
     await auth0.loginWithRedirect(options);
-    expect(mockWindow.location.assign).toHaveBeenCalled();
+    const redirectMethod = options?.redirectMethod || 'assign';
+    expect(mockWindow.location[redirectMethod]).toHaveBeenCalled();
 
     if (error && errorDescription) {
       window.history.pushState(
diff --git a/__tests__/Auth0Client/loginWithRedirect.test.ts b/__tests__/Auth0Client/loginWithRedirect.test.ts
index 2f91db8..b3e88e9 100644
--- a/__tests__/Auth0Client/loginWithRedirect.test.ts
+++ b/__tests__/Auth0Client/loginWithRedirect.test.ts
@@ -69,6 +69,10 @@ describe('Auth0Client', () => {
         assign: {
           configurable: true,
           value: jest.fn()
+        },
+        replace: {
+          configurable: true,
+          value: jest.fn()
         }
       }
     );
@@ -186,6 +190,21 @@ describe('Auth0Client', () => {
       });
     });
 
+    it('should log the user in by calling window.location.replace when redirectMethod=replace param is passed', async () => {
+      const auth0 = setup();
+
+      await loginWithRedirect(auth0, {
+        audience: 'test_audience',
+        redirectMethod: 'replace'
+      });
+
+      const url = new URL(mockWindow.location.replace.mock.calls[0][0]);
+
+      assertUrlEquals(url, TEST_DOMAIN, '/authorize', {
+        audience: 'test_audience'
+      });
+    });
+
     it('should log the user in with custom params', async () => {
       const auth0 = setup();