How to handle situations where the user is authenticated automatically


#1

Given the situation where a user has some sort of token stored in a cookie or local storage, which enables the app to automatically log in and the user calls the page via a link or reloads the browser.

So, given the url http://localhost/books/1 the first problem to me is that I would need to authenticate the user before the model hook is called, as the user will not be able to retrieve the book with id 1 before he is authenticated. However, I am not aware that there is the possibility to authenticate the user in the beforeModel hook, as I would need to wait for the promise of the login to be resolved before I am able to continue to the model hook.

I currently created an auto-login route, where I redirect to in the beforeModel hook, and after the user is authenticated I redirect back to the route the user actually wanted to see. This however feels like a bad workaround. Is there any way to authenticate a user during loading the app?


#2

Don’t know if you’ve seen this yet, but ember-simple-auth is the most popular plugin for authentication in Ember. It will handle token authentication and store it in a cookie/local storage for you, while providing lots of customization through allowing you to write your own authenticators or authorizers.

If you already use ember-simple-auth, take a look at AuthenticatedRouteMixin to make routes force authentication before displaying.


#3

Sorry about the late reply. The AuthenticatedRouteMixin seems to just transition to another route in case the user is unauthenticated. This exactly is my issue. I don’t want to transition to another route. I would like to stay on the route, handle the authentication and then load the model. However this is not quite possible as I would need to wait for the promise from the login is fulfilled before I continue to provide the model.

Currently I am transitioning to another route in case the user is unauthenticated (and has a token stored), which I called auto-login. This route handles the automatic authentication and then transitions back to the previous route automatically. However, this does not seem to be a very nice solution to me, so I am looking for a way to improve that.


#4

In your original post, you’re describing the following situation:

The ember-simple-auth plugin handles storage of authentication tokens for you. This means that when an AuthenticatedRouteMixin route is accessed, ember-simple-auth will automatically check for a stored token; if the token is found, the login information will be automatically retrieved by the plugin and no transition will occur. Thus, a transition will only occur if no authentication token is found – and in this case, you probably want to collect an email/password combination from the user to achieve authentication, which means you need to abort the transition and show a login form instead.

I’m not sure exactly what you were getting at, but remember that Ember can’t really “freeze” in the middle of a transition (i.e. after the transition begins but before the model has been set) to wait for user input. It wouldn’t really make much sense anyway, because the app would be stuck in a state that is not defined by any route, which goes against Ember’s route/model/component/service architecture.

Edit: emphasis


#5

Thank you for the reply. I can see now that this might be somewhat confusing. I think the issue is that I am not storing a token which references the session directly. I am storing a token which can be used to acquire a new session token. So either way the user logs in with the credentials or the persisted token. In both ways the client acquires a session token first, which has a short lifespan and is sent over the wire with each request. The persisted token never gets sent unless the client does not have a session token in which case the old persisted token is invalidated and a new is assigned. So, I need to make that request before being able to fulfil the model. I think I could also work around that by calling refresh on the model after the session token has been acquired. Either way that method hasn’t been there when I checked the last time or I simply overlooked it. However I was hoping there would be an easier way to do that in Ember.


#6

Ah, I see. Well, you proposed two possible solutions, both of which I think you could make work.

This could be accomplished by the route’s beforeModel hook. According to the API, beforeModel can return a promise:

You can return a promise from this hook to pause the transition until the promise resolves (or rejects). This could be useful, for instance, for retrieving async code from the server that is required to enter a route.

So here’s some possible pseudocode for a beforeModel function for authenticated routes:

beforeModel: function() {
  if (/* client does not have a valid session token */) {
    if (/* client has a persisted token */) {
      // use persisted token to get a valid session token, return a promise
    }
    else {
      // client does not have any valid tokens
      // abort transition, redirect to login page
    }
  }
},

Your other suggestion would be possible too:

To get this one to work, just refresh the model after the session token is acquired:

const controller = /* grab controller */,
      route      = /* grab route */,
      param      = /* any params needed to select the model */;

controller.set('model', route.model(params));

#7

Thank you so much for the detailed explanation. It simply never occurred to me that I would be able to return a promise in the beforeModel hook. I think these were the most efficient 5 minutes of refactoring ever :smiley:


#8

Hey there! i was looking into the same thing , but being a noob can’t really figure it out.

I followed ember-simple-auth guidelines and created an app that works authentication-wise. Now i want to load a the model in the application route only if the user authenticates first!

So i understand this is very similar to this solution but can’t connect the dots.

Here’s my application.js route:

import Ember from 'ember';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';
import CheckAdminMixin from '../mixins/check-admin';

export default Ember.Route.extend(ApplicationRouteMixin, CheckAdminMixin, {
	session: Ember.inject.service('session'),
	sessionAccount: Ember.inject.service('session-account'),

	beforeModel() {
		return this._loadCurrentUser();
	},    

	sessionAuthenticated() {
		this._super(...arguments);
		this._loadCurrentUser();
	},

	_loadCurrentUser() {
		return this.get('sessionAccount').loadCurrentUser().catch(() => this.get('session').invalidate());
	},
	model() {
		return this.store.findAll('vessel');
	}
});

#9

not sure what would be the best way to go here, as that depends on what exact behaviour you are looking for… But, what about something like that (disclaimer: never really used ember-simple-auth)?

model() {
  if (userIsAuthenticated) {
    return this.store.findAll('vessel');
  } else {
    return [];
  }
}

However, still might be a good idea to open up a dedicated thread


#10

Hummingbird , thanks so much for trying to answer ,

Problem is that if the application.js route runs one time (where the user is not authenticated) the model() hook runs too. And after that , even if you authenticate , the model() hook wont run again (except if you tell it explicitly but i didn’t feel that was a good practice to refresh())

Also , if you beforeModel() => transitionTo.abort , the page(application templates) wont render anything , and it won’t generally work.

The solution i went for , was to leave the model() , wrap my hbs code around an {{ if session.isAuthenticated}} … {{/if}} so it renders only after the user has authed. And after that , inside my component i used the didInsertElement() method to call the model().