Update: Setting a Current User Property with Ember Simple Auth

A little while back, I share a post on my approach to setting a current user property on an Ember app using the Ember Simple Auth library.

That post received several helpful comments, including one from Marco Otte-Witte, the creator of the Ember Simple Auth library.

So, I thought I'd share his approach here, with a few tweaks of my own.

To get this working, we'll be creating a custom service that requests the current user from our API and sets it equal to an account property. Then, we'll inject this service into our Application route.

Building The Custom Service

Create a file, services/session-account.js.

import Ember from 'ember';

const { inject: { service }, RSVP } = Ember;

export default Ember.Service.extend({
  session: service('session'),
  store: service(),

  loadCurrentUser() {
    return new RSVP.Promise((resolve, reject) => {
      const token =  
        this.get('session.data.authenticated.token');
      if (!Ember.isEmpty(token)) {
        return this.get('store').findRecord('user', 
          'me').then((user) => {
          this.set('account', user);
          resolve();
        }, reject);
      } else {
        resolve();
      }
    });
  }
});

Here, we are injecting the Session and Store services. Then, we define our loadCurrentUser function to introspect on the Session service and extract the current user's authentication token from where it is stored in session.data.authenticated.token.

If a token is present, we know that a current user exists and it interacting with out app. If that is the case, we want to send a request to our API for our current user.

We do so by sending a request to a special current-user-retrieval end-point in our API: http://localhost:3000/users/me. This request is triggered by:

this.get('store').findRecord('user', 'me')

Let's take a look at that end-point in our API:

module Api
  module V1
    class UsersController < ApplicationController

      def me
        render json: current_user
      end
    end
  end
end

And the current_user method in our Application Controller:

class ApplicationController < ActionController::API
  include ActionController::Serialization
  include ActionController::HttpAuthentication::Token::ControllerMethods
 private
    def authenticate!
      authenticate_token || render_unauthorized
    end

    def authenticate_token
      authenticate_with_http_token do |token, options|
         User.find_by(authentication_token: token)
       end
    end

    def render_unauthorized
      render json: {
        errors: ['Bad credentials']
      }, status: 401
    end

    def current_user
      authenticate_token
    end
end

Once the current_user payload is sent back to Ember, we use a promise to set a property, account, equal to that user.

Injecting the Service

Now, we need to inject the service into our Application Route.

import Ember from 'ember';
import ApplicationRouteMixin from 'ember-simple-auth/mixins/application-route-mixin';

const { service } = Ember.inject;

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

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

  sessionAuthenticated() {
    this._loadCurrentUser().then(()=>{
      this.transitionTo('/');
    }).catch(() => this.get('session').invalidate());
  },

  _loadCurrentUser() {
    return this.get('sessionAccount').loadCurrentUser();
  }
  
});

Let's break this down.

  • We've defined a private function, _loadCurrentUser, that introspects on the sessionAcount service we injected, and calls the loadCurrentUser function we defined in that service (see above).
  • We use the sessionAuthenticated hook, which is invoked once the session is successfully authenticated (i.e. the authenticator sent a request to the API and received a payload with the newly signed-in user's email and token). This hook will call our _loadCurrentUser private function and then transition to the root path.
  • We are also using the beforeModel hook (which runs before the model function of any of our routes is invoked) to load the current user when necessary.

Just one more thing!

Injecting session-account into Application Controller

In order to use the sessionAccount property in our application template, we need to inject our session-account service into the Application Controller.

import Ember from 'ember';

export default Ember.Controller.extend({
  sessionAccount: Ember.inject.service('session-account')
});

Using our Current User Property

Now, we can access the current user in our application template like this:

// templates/application.hbs
Hi, {{sessionAccount.account.name}}

subscribe and never miss a post!

Blog Logo

Sophie DeBenedetto

comments powered by Disqus
comments powered by Disqus