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.
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.
- First, install the
devise_token_authgem into your Gemfile.
bundle installto install the gem and its dependencies.
rails g install devise_token_auth:installin 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.
- Update the
- Update the
routes.rbfile to create routes for the authentication actions with
mount_devise_token_auth_for ‘User’, at: ‘auth'. I placed my mount under the namepsace
- Change setting in
config/initializers/devise_auth_token.rbby uncommenting the line
config.change_headers_on_each_request = true.
- In the
include DeviseTokenAuth::Concerns::SetUserByTokento the top.
- Install the
ng-token-authdependency. 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-authas a CDN in your
application.html.erbor include the actual
.jsfile 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.
ng-token-authas a dependency to your
app.jsmodule. 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.
- Configure the
$authProviderthat is available through
ng-token-authto set your apiUrl. The apiUrl should point to the route where your auth actions are located. For most, this would look like
ng-token-authcomes with functions and events accessible through the
$authvariable. I created a separate
AuthCtrlto handle all of the authentication actions and injected
$authas a dependency. The documentation has a full list of available events and functions. At a minimum, you’ll work with
- I used three events to display error messages:
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.error1variable equal to
reason.errorsto 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
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!