New to ember. Questions on authentication with devise/rails


#1

I have a rails API that uses devise with LDAP authentication. This portion of it is working fine. I am selecting to not create users on login, just authenticate with my organizations LDAP server. I hooked up ember with simple auth and thought I had it working. I have been trying to display the username of the logged in person and have not been able to do so.

I put a test on the page like this:

{{#if isAuthenticated}} Yes {{else}} NO {{/if}}

It always sets to NO which leads me to believe that I am not fully authenticating. I checked values in ember inspector of the session and this is what it shows:

emberinspector

So at this point I am confused on if I am authenticated or not in ember? The rails side is working fine. If I do not log in I get redirected to the login page in ember and when I authenticate I get redirected to the proper page. It does seem like it is all working.

I did have to change my devise authenticator to send a username and not email address by adding this identificationAttributeName: ‘username’.

What did I miss here? Sorry this is my first ember app. I don’t really know how to run this one down.


#2

So when you have this in your template:

{{#if isAuthenticated}} Yes {{else}} NO {{/if}}

It’s looking for the property “isAuthenticated” on the controller of whatever route you are working with (I’ll assume it’s in your application template so it would be looking for that property defined on your application controller). The important thing to remember about using data in your template is that you have to make sure it’s available to the template. A route’s model is injected into the controller automatically as model. Pretty much everything else you must define/inject yourself.

So how do you access properties on the simple auth “session” service from the application template? First, you want to inject the service into your application controller. To do that it would look something like this:

// Ember 2.15+ (modules)
import Controller from '@ember/controller';
import { inject as service } from '@ember/service';

export default Controller.extend({
  session: service(),
  ...
});

// Ember < 2.15
import Ember from 'ember';

export default Ember.Controller.extend({
  session: Ember.inject.service(),
  ...
});

Then you change your template code to something like this:

{{#if session.isAuthenticated}} Yes {{else}} NO {{/if}}

So, in summary: templates have access to whatever is defined/injected on their matching controller (if a route template) or component (if a component template). You can either define properties or computed properties on the controller/component, or inject services, etc into the controller/component. For routes, the model is injected automatically into the controller.


#3

Thank you for getting back to me. I appreciate it. That makes sense. I did not even have an application controller. I created one and am able to display the currentUser name correctly. Is there anywhere that you can recommend to show what useful site wide functions that should be in an application.js file? Here is what I have. Does this look OK or should I change it.

import Controller from '@ember/controller';
import { inject as service } from '@ember/service';

export default Controller.extend({
  session: service(),
  currentUser: service(),

  currentUser: Ember.computed('session.data', function() {
   return this.get('session.data.authenticated');
 }),
 
 beforeModel() {
   return this._loadCurrentUser();
 },

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

 _loadCurrentUser() {
   return this.get('currentUser').load().catch(() => this.get('session').invalidate());
 }
});

#4

Looks like you’re on the right track but I see a couple issues here:

  • you have currentUser defined twice, once as an injected service and once as a computed property. You could rename the CP, or you could rename the service (e.g. currentUserService: service('current-user') or something like that).
  • you’re using “beforeModel” which is only fired on a route (nothing to stop you from defining a beforeModel hook on a controller, but it won’t be fired automatically and it doesn’t make a lot of sense as the model hooks are all handled on the route.
  • think sessionAuthenticated is overriding the ‘AuthenticatedRouteMixin’ from simple auth, which should be done on the application route, not the application controller

A brief overview of the difference between routes and controllers and what you should use them for:

  • Route: should be used to handle fetching the data for the view and handling any transition-related items
  • Controller: should basically be used to expose things to the template and handle actions (though the route can handle actions as well)

So let’s say you have a view for displaying a list of users. You’d probably call it “users”. So you’d have an entry in your router.js. Then you’d have a “users” route aka routes/users.js. Then you’d have a template which renders that route in /templates/users.hbs. Those two files would be generated if you ran ember g route users for example. The controller is optional (Ember automagically creates one and injects the model into it if you don’t specify one). If you wanted to have some CPs that the template uses, like say sorting the list of users by last name, you could define them on the controller, and then use those CPs in the template. The controller could also handle any actions you define, like maybe changing the sort field or adding a new user. To generate a blank controller for users you’d just run ember g controller users.

In the above users example, you’d typically fetch the users data from your API in the route’s model hook (let’s say you returned this.store.findAll('user') from the model hook), and then the model would be automatically injected on the controller as model. Then in the controller if you wanted to sort the users, for example, you could say:

...
sortDef: ['last_name:desc'],
sortedUsers: computed.sort('model', 'sortDef'),
...

Then in your template you could render the users with something like:

{{#each sortedUsers as |user|}}
  <li>{{user.first_name}} {{user.last_name}} - {{user.email}}</li>
{{/each}}

So in the above example you use the route to fetch the data, the controller to munge the data and/or add some more state, and then the template to render things that are defined on the controller, extra markup, etc.

As for what makes sense to put in the application route/controller/model? In the route you’d probably want to do any application-level setup, fetch any “global” data, handle global app errors, or what have you. In the controller you’d want to define any properties or inject any services that the application template will need (like in the original post) and then in the template you’d typically render global application markup, like navbars or headers/footers that belong on every page, etc.


#5

Thanks a lot this has been very helpful. I sorted out the duplicate current_user and made a couple of other tweaks.

One more question. How do I invalidate or kill the login session after a timeout? I have devise setup to do that after 15 minutes but it doesn’t carry over to the ember side. Do I need to send that over from rails or do something on the ember side separate from rails? In testing right now I am not hitting any rails controllers, just simple ember pages with HTML.


#6

So ember simple auth should handle that for you. Basically if your session times out and then you make a subsequent request the backend would typically return a 401 Unauthorized. ESA intercepts this and then should invalidates the session automatically.

If you want to handle this manually you can use the invalidate method on the session service. As for where to do that… really depends on how you want it to work. Once you start making requests to your backend I think the session invalidation will probably work how you expect. If not feel free to describe what you’re looking to do here and I can try and help you figure out where to put the code for best effect.


#7

So there are no parts of this application that are public facing. So I want people to authenticate to do anything in the site. So I am looking for something at the application level to check every request to see if the current user is authenticated and if not bounce them back to the login page. Also am looking to set a timeout and have the session deleted after the timeout so the next time the person clicks or refreshes or anything they get redirected to the login page.

I have a logout button that kills the session, I’m just not sure how to set the timeout up. Also on browser close I would like the session destroyed.

Here is what I have so far if interested.


#8

So there are no parts of this application that are public facing. So I want people to authenticate to do anything in the site. So I am looking for something at the application level to check every request to see if the current user is authenticated and if not bounce them back to the login page.

This part should be achievable entirely by the AuthenticatedRouteMixin in ember simple auth. Basically you’ll just need to add it to every route except the application route (which has it’s own mixin) and whatever route you want to use for the actual login process. So that’s good news, should basically just work.

Also am looking to set a timeout and have the session deleted after the timeout so the next time the person clicks or refreshes or anything they get redirected to the login page.

This would essentially be the effect of what I described previously. Depends on a little bit on your backend (assumes it returns a 401 if any requests are made with an expired session/token) but basically the session would expire, the client wouldn’t necessarily know that (depending on what authenticator you’re using, I think for Password Grant auth it does take expiration into account in ember simple auth) and then the client would make a request on user click/refresh, get a 401 response, and ember simple auth would forward the user to whatever you have defined as your “authenticationRoute”.

I’ll take a look at the code shortly and see if I can make any other recommendations!


#9

Thanks for the info. I appreciate the help.