Return to route after login using navigation instruction


#1

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 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();
    }
}

#2

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.

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 :slight_smile:


#3

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));
    }
  }
}