Any Software as a Service will likely need the capability to authenticate a user and show information about that user. As an interesting design choice, Ember does not include authentication as a core feature. Instead, Ember developers must turn to the addon ecosystem. After some research, I chose Ember Simple Auth with Ember Simple Auth Token to use JSON Web Tokens (JWT) for College Conductor. JSON Web Tokens are an emerging standard that make secure exchanges (like authentication) possible. To provide user information throughout the application, I had to do some extra work to create an Ember service. I’m going to cover what I learned in the process.
Thankfully for me (and you),
Ember Simple Auth has good documentation
to cover a wide variety
of uses.
As a template,
I used the
Managing a Current User
guide
to direct the service
that I needed.
This guide lays out the structure required
to extract information
from the session
service provided
by Ember Simple Auth.
Generating a service to start is one command away:
$ ember generate service current-user
We can begin by looking at the code in the Ember Simple Auth guide.
import Ember from 'ember';
const { inject: { service }, isEmpty, RSVP } = Ember;
export default Ember.Service.extend({
session: service('session'),
store: service(),
load() {
let userId = this.get('session.data.authenticated.user_id');
if (!isEmpty(userId)) {
return this.get('store').findRecord('user', userId).then(
(user) => {
this.set('user', user);
});
} else {
return Ember.RSVP.resolve();
}
}
});
This service is almost exactly what we need but there is a problem. When using JWT, the data stored in the session is an encoded token. The service needs to get the user ID and it’s not easily accessible. That leads to code like:
import Ember from 'ember';
const { inject: { service }, isEmpty, RSVP } = Ember;
export default Ember.Service.extend({
session: service(),
store: service(),
load() {
const token = this.get('session.data.authenticated.token');
if (!isEmpty(token)) {
const userId = this.getUserIdFromToken(token);
return this.get('store').find('user', userId).then((user) => {
this.set('user', user);
});
} else {
return RSVP.resolve();
}
},
getUserIdFromToken(token) {
// What goes here?
return 42;
}
});
As I explored the source code,
I discovered that the session did not store the decoded token data.
I had to find a way to decode the token
so I could extract the user_id
that my API provided.
The JWT authenticator
in Ember Simple Auth Token
included what I needed.
Ember Simple Auth does not expose
a method to get the authenticator
(please prove me wrong!)
so I opted to create an instance myself
and invoke its getTokenData
method.
The result looks like:
import JWT from 'ember-simple-auth-token/authenticators/jwt';
<snip>
getUserIdFromToken(token) {
const jwt = new JWT();
const tokenData = jwt.getTokenData(token);
return tokenData['user_id'];
}
If I reflect on what I had to do, I think it is safe to state that the final change was not too difficult. The part that I find interesting is that I had to be comfortable digging through the source code of Ember Simple Auth Token.
Reading other source code is a needed developer skill.
Last week, I wrote about software abstractions and the benefits of boundaries. The boundaries provided by abstractions are extremely useful, but we should be comfortable breaking through them when it seems like there are no options. Reading the source code of third party addons, extensions, plugins, and the like is a useful tool that can often make quick work of a tricky problem.