Implementing Devise (Rails) with Angular

Working strictly with Rails introduced me to the Devise authentication gem quite some time ago. While it’s definitely possible to write your own authentication system for your Rails app, Devise makes the process so much easier so you can focus on building out other features. When I started with my Rails API /Angular app, I knew I wanted to leverage the functionality of Devise, but how do I get Devise to communicate with a login page that lives on the Angular front-end?

Luckily, I’m not the first person to have this problem. Indeed, there is a set of gems/dependencies available that works to solve this communication problem via tokens. Both of these were written by Lynn Dylan Hurley: the Rails gem devise_token_auth and the Angular module ng-token-auth. Using the two of these concurrently gives you a powerful solution to use Devise with Angular. The READMEs for both of those are very robust, so the steps below are specifically what I did to get the authentication up and running.

The Feature

I have a single User model and require the ability to allow users to register and log into their accounts. I need to pass the user registration/login information (email address, username, password) from a form (Angular template) into Devise, authenticate with my database, and return the authenticated user (in the form of a token) back to the browser.

Rails Integration

  1. First, install the devise_token_auth gem into your Gemfile.
  2. Run bundle install to install the gem and its dependencies.
  3. Run rails g install devise_token_auth:install in the terminal. The docs also show you how you can create a User model and routing at the same time, but I didn’t do that because I already had my own User model and routing.
  4. Update the User model with include DeviseTokenAuth::Concerns::User.
  5. Update the routes.rb file to create routes for the authentication actions with mount_devise_token_auth_for ‘User’, at: ‘auth'. I placed my mount under the namepsace api.
  6. Change setting in config/initializers/devise_auth_token.rb by uncommenting the line config.change_headers_on_each_request = true.
  7. In the application_controller.rb, add include DeviseTokenAuth::Concerns::SetUserByToken to the top.

Angular Integration

  1. Install the ng-token-auth dependency. The tutorials recommend installing with Bower, a manager that handles all front end assets. In the early stages of my app, I opted to not use Bower and to place all my assets in the vendor/assets directory. Two options are available if you also choose to not use Bower: include ng-token-auth as a CDN in your application.html.erb or include the actual .js file in your vendor/assets directory. I chose to do the latter. Download the files from GitHub and pull the file that you want (full or min) that you need into your vendor/assets folder.
  2. Inject ng-token-auth as a dependency to your app.js module. When I first included just ng-token-auth, I got an Angular error in the browser for a missing dependency called ipCookie. If you encounter this same error, download and add the file to your vendor/assets, then inject ipCookie to your dependency list.
  3. Configure the $authProvider that is available through ng-token-auth to set your apiUrl. The apiUrl should point to the route where your auth actions are located. For most, this would look like apiUrl: '/api'.
  4. ng-token-auth comes with functions and events accessible through the $auth variable. I created a separate AuthCtrl to handle all of the authentication actions and injected $auth as a dependency. The documentation has a full list of available events and functions. At a minimum, you’ll work with submitLogin(), submitRegistration(), and signOut().
  5. I used three events to display error messages: auth:login-error, auth:registration-email-error, and auth:logout-error. Just attach the event to $scope.$on(auth:login-error) and have your code display messages as needed. An example of my error event is below. Errors are returned in the browser as an array object, so to retrieve just the message, I set the $scope.error1 variable equal to reason.errors[0] to return the error message (at 0-index in the array).

Putting It All Together

Now, all you need to do is build the HTML forms to take in the information. I placed my login and registration forms on the same page for simplicity.

The form uses ng-submit to call the correct function from the AuthCtrl. ng-init creates a model object to attach attributes such as email and password. I have a few ng-show for the different possible errors that may pop up.

Once I got ng-token-auth working as an asset, configuration between Rails and Angular went much more smoothly. If you’re researching how to implement authentication for Rails + Angular, highly recommend these two gems/modules!