# Return to route after login using navigation instruction

**URL:** <https://discourse.aurelia.io/t/return-to-route-after-login-using-navigation-instruction/277>\
**Category:** Help Requests\
**Created:** [November 1, 2017, 9:51pm UTC](https://discourse.aurelia.io/t/return-to-route-after-login-using-navigation-instruction/277 "2017-11-01T21:51:17Z")\
**Posts on this page:** 7\
**Page:** 1

<div class="post-metadata">

**Author:** ![winter\_limelight](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/winter_limelight/32/226_2.png) [@winter\_limelight](https://discourse.aurelia.io/u/winter_limelight)\
**Post date:** [November 1, 2017, 9:51pm UTC](https://discourse.aurelia.io/t/return-to-route-after-login-using-navigation-instruction/277/1 "2017-11-01T21:51:17Z")

</div>

I’m trying to set up a failed-auth post-login redirect.

My plan is (was) that when authentication fails, the intended (i.e. requiring auth) navigation instruction is saved, and next time the user turns up logged in, then it would run the saved navigation instruction.  
However I can’t find any method that allows me to either replace the navigation instruction with a saved one, or just ‘run’ the instruction.

Is this an appropriate way to approach this problem in Aurelia? If not, what would be recommended?

I did find [https://stackoverflow.com/questions/42591682/how-to-return-to-view-after-authentication-in-aurelia](https://stackoverflow.com/questions/42591682/how-to-return-to-view-after-authentication-in-aurelia) but that didn’t help me with the query string (altho a small amount of url manipulation would solve that), but I was hoping it was possible to use the instruction directly.

Here is a cutdown version of the ts code:

```
export class NotAuthorizedRedirect {
    private navigationInstruction: NavigationInstruction;

    public notAuthorized(from: NavigationInstruction) {
        this.navigationInstruction = from;
        return new Redirect("login-route");
    }

    public getNavigationInstruction() {
        let instruction = this.navigationInstruction;
        this.navigationInstruction = null; // single use
        return instruction;
    }
}

@autoinject
class AuthorizeStep {
    constructor(private redirect: NotAuthorizedRedirect) { }

    run(navigationInstruction, next) {

        let isLoggedIn = authMethod(...);
        if (!isLoggedIn) {
            return next.cancel(this.redirect.notAuthorized(navigationInstruction));
  
        let preAuthNavigatonInstruction = this.redirect.getNavigationInstruction();
        if(preAuthNavigatonInstruction) {
            // do something with preAuthNavigatonInstruction that includes querystring parameters
        }

        next();
    }
}
```

---

<div class="post-metadata">

**Author:** ![winter\_limelight](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/winter_limelight/32/226_2.png) [@winter\_limelight](https://discourse.aurelia.io/u/winter_limelight)\
**Post date:** [November 2, 2017, 7:18pm UTC](https://discourse.aurelia.io/t/return-to-route-after-login-using-navigation-instruction/277/2 "2017-11-02T19:18:35Z")

</div>

I’ve gone with the following in place of the ‘do something with preAuthNavigationInstruction’ comment, and so far so good. This uses the feature that params not listed in the route become appended in the query string.

```javascript
let redirectUri = preAuthNavigatonInstruction.router.generate(
    preAuthNavigatonInstruction.config.name,
    Object.assign(preAuthNavigatonInstruction.params, preAuthNavigatonInstruction.queryParams),
    { replace: true });

return next.cancel(new Redirect(redirectUri));

```

Now I just want to add something to my login page to indicate a redirect is in progress and I’ll be happy 🙂

---

<div class="post-metadata">

**Author:** ![fragsalat](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/fragsalat/32/270_2.png) [@fragsalat](https://discourse.aurelia.io/u/fragsalat)\
**Post date:** [November 9, 2017, 11:02am UTC](https://discourse.aurelia.io/t/return-to-route-after-login-using-navigation-instruction/277/3 "2017-11-09T11:02:44Z")

</div>

Here is my solution:

1. I add an Authorization step to the pipeline
2. The authorization step checks if a login is required and if the user then is logged in  
2.1. The step saves the current url by taking instructions fragment and query  
2.2. A Redirect to the login page is created and returned which aborts the pipeline
3. If no authorization is required or the user is logged in I check if a origin URL was saved  
3.1. Check If the current instruction fragment is not the login page and I have a origin  
3.2. Create a redirect to the origin and cancel the pipeline
4. Done

Here’s are the relevant code parts of my AuthorizationStep  
import {Redirect} from ‘aurelia-router’;

```
export class AuthorizationStep {

  static loginFragment = '/login';

  run(instruction, next) {
    return Promise.resolve()
      .then(() => this.checkAuthorization(instruction, next))
      .then(result => result || this.checkOrigin(instruction, next))
      .then(result => result || next());
  }

  checkAuthorization(instruction, next) {
    if (instruction.getAllInstructions().some(i => i.config.auth)) {
      if (!isLoggedIn()) {
        const currentUrl = instruction.fragment + (instruction.queryString ? `?${instruction.queryString}` : '');
        localStorage.setItem('origin', currentUrl);
        return next.cancel(new Redirect(AuthorizationStep.loginFragment));
      }
    }
  }

  checkOrigin(instruction, next) {
    const origin = localStorage.getItem('origin');
    // Check if we were not redirected to login page and have an origin
    if (instruction.fragment !== AuthorizationStep.loginFragment && origin) {
      localStorage.removeItem('origin');
      return next.cancel(new Redirect(origin));
    }
  }
}
```

---

<div class="post-metadata">

**Author:** ![airboss001](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/airboss001/32/719_2.png) [@airboss001](https://discourse.aurelia.io/u/airboss001)\
**Post date:** [April 5, 2018, 1:32am UTC](https://discourse.aurelia.io/t/return-to-route-after-login-using-navigation-instruction/277/4 "2018-04-05T01:32:54Z")

</div>

Just wanted to add this for others that might run into the same issue I had.

This worked really well, but when using Auth0, or another authentication service that uses callbacks be sure to add that fragment to ignore along side the loginFragment or you end up getting multiple logins cycles and lose the origin page.

---

<div class="post-metadata">

**Author:** ![avrahamcool](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/avrahamcool/32/368_2.png) [@avrahamcool](https://discourse.aurelia.io/u/avrahamcool)\
**Post date:** [September 11, 2020, 11:15am UTC](https://discourse.aurelia.io/t/return-to-route-after-login-using-navigation-instruction/277/5 "2020-09-11T11:15:47Z")

</div>

Thanks for that snippet of code.

I just want to add my _two-penny worth_ idea,  
use `sessionStorage` instead of `localStorage`, for the case where multiple tabs are opened at once with different routes - each one will redirect to its own target after login.

---

<div class="post-metadata">

**Author:** ![Bart](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/bart/32/1878_2.png) [@Bart](https://discourse.aurelia.io/u/Bart)\
**Post date:** [September 14, 2020, 8:18am UTC](https://discourse.aurelia.io/t/return-to-route-after-login-using-navigation-instruction/277/6 "2020-09-14T08:18:12Z")

</div>

I got some inspiration from this article:

> **[Aurelia Authentication: Multiple shells.](http://www.foursails.co/blog/aurelia-login-best-practices-pt-1/)**
>
> Creating a separate login-specific Aurelia root within your Aurelia app.

It comes down to using the `setRoot` method of the `Aurelia` instance where needed. By default, it is set to the `app` component during startup, but you can use it anywhere you want and specify any desired component, if you use dependency injection to get the `Aurelia` instance.

Any specified url remains intact during login. After login, Aurelia will automatically navigate to the desired page.

---

<div class="post-metadata">

**Author:** ![zewa666](https://yyz1.discourse-cdn.com/flex027/user_avatar/discourse.aurelia.io/zewa666/32/19_2.png) [@zewa666](https://discourse.aurelia.io/u/zewa666)\
**Post date:** [September 14, 2020, 11:25am UTC](https://discourse.aurelia.io/t/return-to-route-after-login-using-navigation-instruction/277/7 "2020-09-14T11:25:31Z")

</div>

I second this. Used that approach now for the second time and had very good experiences with it.

Essentially my workflow looks something along these lines:

```auto
  // main.ts
  aurelia.use.plugin("aurelia-store", {
    initialState
  });

  await aurelia.start();
  const auth = aurelia.container.get(AuthService);
  const user = await auth.getLoggedInUser();

  const store = aurelia.container.get(Store);
  store.registerAction("Set User", setUser);

  if (!user) {
    // go to login screen, while maintaining the original location in the address bar
    aurelia.setRoot("components/login");
  } else {
    await store.dispatch(setUser, user);
    // already logged in, keeping the address bar
    aurelia.setRoot("app");
  }

// login.ts
  public async login() {
    try {
      await this.auth.login(this.emailUsername, this.password);

      // redirect back to app while still keeping the location in the address bar intact
      this.aurelia.setRoot("./app");
    } catch (e) {
      // tslint:disable-next-line:no-console
      console.error(e);
    }

    return false;
  }

```
