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
- First, install the
devise_token_authgem into your Gemfile. - Run
bundle installto install the gem and its dependencies. - Run
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
Usermodel withinclude DeviseTokenAuth::Concerns::User. - Update the
routes.rbfile to create routes for the authentication actions withmount_devise_token_auth_for ‘User’, at: ‘auth'. I placed my mount under the namepsaceapi. - Change setting in
config/initializers/devise_auth_token.rbby uncommenting the lineconfig.change_headers_on_each_request = true. - In the
application_controller.rb, addinclude DeviseTokenAuth::Concerns::SetUserByTokento the top.
Angular Integration
- 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: includeng-token-authas a CDN in yourapplication.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. - Inject
ng-token-authas a dependency to yourapp.jsmodule. When I first included justng-token-auth, I got an Angular error in the browser for a missing dependency calledipCookie. 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 throughng-token-authto set your apiUrl. The apiUrl should point to the route where your auth actions are located. For most, this would look likeapiUrl: '/api'. ng-token-authcomes with functions and events accessible through the$authvariable. I created a separateAuthCtrlto 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 withsubmitLogin(),submitRegistration(), andsignOut().- I used three events to display error messages:
auth:login-error,auth:registration-email-error, andauth: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 toreason.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!
