mainmatter / ember-simple-auth

A library for implementing authentication/authorization in Ember.js applications.
https://ember-simple-auth.com
MIT License
1.93k stars 606 forks source link

captured attemptedTransition retry always fails #2287

Open arenoir opened 3 years ago

arenoir commented 3 years ago

Since upgrading to version 3.1 my application fails to retry the pre login captured transition.

The attemptedTransition is set in the session service but it always fails on retry.

Any ideas as to why all attemptedTransition fails?

I have hacked the handleAuthentication hook to transition to the captured named route but this is not a solution.

import { inject as service } from '@ember/service';
import Service from 'ember-simple-auth/services/session';

export default class SessionService extends Service {
  @service router;
  handleAuthentication() {         
    if (this.attemptedTransition) {
      this.router.transitionTo(this.attemptedTransition.to.name);
    } else {
      this.router.transitionTo('/');
    }
  }
}

Test that was passing using version ember-simple-auth 3.0.

  test('visiting /jobs redirects to login page and upon successful login redirects to jobs page ', async function (assert) {
    mockValidLogin();
    await visit('/jobs');
    assert.equal(currentURL(), '/login');
    await fillIn('input', 'aaron');
    await fillIn('input[type=password]', 'password');
    await click('button[type=submit]');
    assert.equal(currentURL(), '/jobs');
  });
arenoir commented 3 years ago

Something else to note I am requiring authentication in the the application route beforeModel hook like so.

  beforeModel(transition) {
    if (transition.targetName !== 'login') {
      this.session.requireAuthentication(transition, 'login');
    }
  }
marcoow commented 3 years ago

I cannot reproduce this. Would you be able to set up a reproduction app?

Also be aware that the application route's beforeModel route is only called when you visit the first route in the app/when the app boots up. It will not be called when you transition between routes after that so you'd still have to explicitly requireAuthentication for all routes in your app that require the session to be authenticated.

sacarino commented 3 years ago

@arenoir just curious, are you using a custom authenticator? I'm wiring up to auth0-js and running into the same thing. Since auth0 has their hosted login page and then it sends the user to a callback, the attemptedTransition is lost in the page reload.

arenoir commented 3 years ago

@sacarino, yes I am using a custom authenticator. This is a good bit of information. I haven't had much time to investigate but let me know if you get anywhere.

sacarino commented 3 years ago

Okay - at least in my case it's definitely an OAuth2 thing - a few other issues refer to it. Here's a Google OAuth2 one, but there's others.

Found an interesting comment about overriding ESA's cookie, but that doesn't work in this case since the page reload wipes out the session. As a result, whatever you had manually set becomes the callback's route as soon as they hit it.

It was nudge in a useful direction though... using ember-cookies, this rough take is working.

Worth mentioning that I have a base route that all my other routes extend, to address @marcoow's point

It will not be called when you transition between routes after that so you'd still have to explicitly requireAuthentication for all routes in your app that require the session to be authenticated.

baseRoute.js

@service session;
@service cookies;

beforeModel(transition) {

   ... other code

   if (!this.session.isAuthenticated) {
     this.saveDestination(transition);
   }

   this.session.requireAuthentication(transition, 'login');
},

saveDestination(transition) {
    if (transition.to.name !== 'login' &&
    transition.to.name !== 'callback') {
      // User has a preferred deep link
      let cookiesService = this.cookies;

      if (cookiesService.exists('desired-redirectTarget')) {
        // this workaround courtesy of firefox
        // https://github.com/simplabs/ember-cookies/issues/152#issuecomment-377833568
        cookiesService.write('desired-redirectTarget', '', { path: '/', expires: new Date().toISOString() });
        cookiesService.clear('desired-redirectTarget');
      }
      cookiesService.write('desired-redirectTarget', transition.intent.url, { path: '/', expires: new Date().toISOString() });
    }
  },

and then in my callback's route, which does NOT extend baseRoute

@service session;
@service cookies;

beforeModel() {
    // inside the callback route
    // try to authenticate
    // then send us to home (or their happy place, if exists)
    this.session
      .authenticate('authenticator:auth0')
      .then(() => {
        let cookiesService = this.cookies;
        let desiredUrl = cookiesService.read('desired-redirectTarget');

        if (desiredUrl) {
          this.transitionTo(desiredUrl);
          cookiesService.clear('desired-redirectTarget');
        } else {
          this.transitionTo('home')
        }
      });
  }

Hopefully that helps you solve your issue as well.