Sunday, October 5, 2014
Authentication with EmberJS - Part 3
If you have not yet gone through Part 1 and Part 2, I recommend you do. You can check out the code up to this point with the following:
$ git clone https://github.com/cavneb/simple-auth.git simple_auth$ cd simple_auth$ git checkout part-2-completed$ bundle install$ rake db:migrate; rake db:migrate RAILS_ENV=test$ rake testAlso, make sure you run ./bin/ember_build.sh in a separate tab.
So far, we have created an Ember application with a RailsAPI backend and can register, login and logout. There are a few more things that we want to be able to do before we can call it a wrap on this series.
Pass the access token with each request to the backend and require authorization for some data to return.Force the user to the login page when they try to access a page which requires authentication.Add validation to our registration form.Believe it or not, this is already happening. If you look at auth_manager.js, you will see that in the authenticate function, we add the headers to each AJAX request with the access token.
Let’s test this.
Open up the top_secret route and load place the user list into the controllers model:
public/javascripts/routes/top_secret_route.js var User = require('../models/user');var TopSecretRoute = Ember.Route.extend({ model: function() { return User.find(); }});module.exports = TopSecretRoute;Now update the top_secret template with the following:
public/javascripts/templates/top_secret.hbs 1234567891011121314151617181920Users (Top Secret Stuff)
Name | Username | |
---|---|---|
{{name}} | {{email}} | {{username}} |
Refresh the browser and click on the Top Secret nav item. If you haven’t already registered and/or logged in, do it first.
If you view the console when loading this page, you will see the network request made to /users. In this request, you can see the headers sent out, one of which is the Authorization header. If this wasn’t there, we would not be able to see a list of users. Click ‘Logout’ and then go to the Top Secret page again. See? You get a 401 Unauthorized response from /users.
If you still see the users list, it is because they are cached. Refresh the page.It seems rather silly for us to be able to click on the Top Secret nav item and see an empty list of users. Let’s require the user to be authenticated in order to view that page.
The easiest way to do this is to create a base route which can be extended by routes which require authentication. Create a new route called authenticated.
$ ember generate -r authenticatedpublic/javascripts/routes/authenticated_route.js 1234567891011121314151617181920212223var AuthenticatedRoute = Ember.Route.extend({ beforeModel: function(transition) { if (!App.AuthManager.isAuthenticated()) { this.redirectToLogin(transition); } }, // Redirect to the login page and store the current transition so we can // run it again after login redirectToLogin: function(transition) { var sessionNewController = this.controllerFor('sessions.new'); sessionNewController.set('attemptedTransition', transition); this.transitionTo('sessions.new'); }, events: { error: function(reason, transition) { this.redirectToLogin(transition); } }});module.exports = AuthenticatedRoute;Now modify the sessions/new controller and redirect to the attemptedTransition if available:
public/javascripts/controllers/sessions/new_controller.js 1234567891011121314151617181920212223var SessionsNewController = Ember.ObjectController.extend({ attemptedTransition: null, loginUser: function() { var self = this; var router = this.get('target'); var data = this.getProperties('username_or_email', 'password'); var attemptedTrans = this.get('attemptedTransition'); $.post('/session', data, function(results) { App.AuthManager.authenticate(results.api_key.access_token, results.api_key.user_id); if (attemptedTrans) { attemptedTrans.retry(); self.set('attemptedTransition', null); } else { router.transitionTo('index'); } }); }});module.exports = SessionsNewController;Finally, update the top_secret route to extend the new AuthenticatedRoute:
public/javascripts/routes/top_secret_route.js var AuthenticatedRoute = require('./authenticated_route');var User = require('../models/user');var TopSecretRoute = AuthenticatedRoute.extend({ model: function() { return User.find(); }});module.exports = TopSecretRoute;Refresh your browser and click on the Top Secret nav item. You should be redirected to the login page. Now log in and it should redirect you right back to the top secret page.
The final thing I am going to cover (briefly) is performing form validation. This is very simple considering our backend is already giving us what we need. Open up the user.js model and add ‘errors’ as an attribute:
public/javascripts/models/user.js var User = DS.Model.extend({ name: DS.attr('string'), email: DS.attr('string'), username: DS.attr('string'), errors: {}});module.exports = User;Now open up the users/new controller and capture the errors on a failed registration and place them into the errors hash:
public/javascripts/controllers/users/new_controller.js 1234567891011121314151617181920var UsersNewController = Ember.ObjectController.extend({ createUser: function() { var router = this.get('target'); var data = this.getProperties('name', 'email', 'username', 'password', 'password_confirmation') var user = this.get('model'); $.post('/users', { user: data }, function(results) { App.AuthManager.authenticate(results.api_key.access_token, results.api_key.user_id); router.transitionTo('index'); }).fail(function(jqxhr, textStatus, error ) { if (jqxhr.status === 422) { errs = JSON.parse(jqxhr.responseText) user.set('errors', errs.errors); } }); }});module.exports = UsersNewController;Finally, update the registration template:
public/javascripts/templates/users/new.hbs 123456789101112131415161718192021222324252627282930313233343536Register
Refresh the browser and play around with the form. You should see error messages on submit if they are invalid. These errors are provided by Rails on the backend and are returned via the API.
Thanks for sticking through this with me. The final code can be found at https://github.com/cavneb/simple_auth.
I especially want to thank all those who are taking the time to teach Ember via blogs, screencasts and live presentations. I find myself struggling less and less every day because new content comes out from all of you. Thanks!
No comments: