TD;DR: We’ll create a simple Rails & Angular (2+) web application that allows a user to create an account, login with it and view his profile using Devise and token authentication.
Final result
This tutorial assumes you’re comfortable with Ruby on Rails (RoR), more precisely with the version 5 and Rails API Mode, and somewhat familiar with Angular 2+. This is not exactly a “Getting Started with Rails and Angular” kind of tutorial for absolute beginners, but I’ll go into as much details as I can.
I’ll also assume you have NodeJS 6.9+, NPM and Postgres installed on your system, if not, there are great tutorials online if you Google around.
Frontend and backend will be running on separate servers, communicating with each other via a REST API.
Before we begin, English is not my first language, so if you see something weird, please, calmly point it out in the comment section, there’s no need for violence :)
If you don’t want/need to go through this process, you can just download the final code from this repo: https://github.com/avatsaev/rails-devise-token-seed and jump straight to Part 2: Bootstrapping and configuring the frontend.
Let’s get started.
First let’s bootstrap a new Rails project in API mode and Postgres database (DB) config:
$ rails new rails_devise_token_auth --api --database=postgresql
This will create a barebones Rails 5 app in API mode (without views and without the asset pipeline, JSON will be the default response format)
Next, we’ll add a few useful gems to our Gemfile:
gem 'devise_token_auth'
gem 'omniauth'
gem 'rack-cors', :require => 'rack/cors'
Devise Token Auth leverages Devise and will allow us to do user management via a REST API, omniauth is a devise_token_auth dependency.
We need rack-cors, which allows us to do cross domain AJAX requests (because we are running in API mode, so the backend will most certainly run on a separate server and domain when in production).
Install the gems:
$ bundle install
Let’s configure cors, edit the file ./config/initializers/cors.rb (create if doesn’t exist), replace its contents with the following:
Rails.application.config.middleware.use Rack::Cors do
allow do
origins '*'
resource '*',
:headers => :any,
:expose => ['access-token', 'expiry', 'token-type', 'uid', 'client'],
:methods => [:get, :post, :options, :delete, :put]
end
end
For this tutorial we’ll allow all domains to contact our server, expose some custom headers retuned by Devise Token Auth, and allow the most commonly used http request methods, although we won’t need all of them.
Assuming you already have a Postgres server installed and running on your system, we do not have to change anything, because Postgres is already initialised with a public postgres user and database, and initial Rails DB config uses postgres as default user and localhost as default host, but if your Postgres server has a custom password protected user, don’t forget to edit the ./config/database.yml file:
default: &default
adapter: postgresql
encoding: unicode
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
development:
<<: *default
database: rails_devise_token_seed_development
username: YOUR_CUSTOM_USERNAME
password: YOUR_PASSWORD
test:
<<: *default
database: rails_devise_token_seed_test
username: YOUR_CUSTOM_USERNAME
password: YOUR_PASSWORD
If you read the DeviseTokenAuth README you’ll see how to initialise our user model:
$ rails generate devise_token_auth:install User auth
User will be our model name, and auth will be the endpoint of our user management rest api (ex: /auth/sign_in, /auth/sign_out)
Generated output of this command should look something like this:
create config/initializers/devise_token_auth.rb
create db/migrate/20170131161242_devise_token_auth_create_users.rb
create app/models/user.rb
insert app/controllers/application_controller.rb
gsub config/routes.rb
The most interesting files for us are highlighted in bold.
The first one is a database migration file that will initialise the users table for us with all required fields.
We also have User model initialised, as well as the auth endpoint mounted in routes.rb
For this tutorial, we’ll disable account email validation by opening the User model (./app/models/user.rb) file and removing confirmable module from the devise configuration, so the User model should look something like this:
class User < ActiveRecord::Base
devise :database_authenticatable,
:registerable,
:recoverable,
:rememberable,
:trackable,
:validatable,
:omniauthable
include DeviseTokenAuth::Concerns::User
end
We’ll initialise the users table with a default user that will allow us to test our API as we go. In ./db/seeds.rb add the following line:
User.create(email: '[email protected]', nickname: 'UOne', name: 'User One', password: "monkey67")
Our login will be [email protected] with password monkey67
$ rails db:create && rails db:migrate && rails db:seed
This one liner will create the test and dev databases, migrate the users table, and create our default user in it:
Now we can finally start our backend server and test the API, start the Rails server by issuing the following command:
$ rails s
And we’ll try to login with our default user credentials, to test REST APIs I use the Paw App for macOS (which is a great tool btw, you if you work with REST APIs, I can’t recommend it enough) but you can use whatever tool you like or just curl in your terminal.
By reading the Devise Token Auth documentation we can see that we need to make a POST request to /auth/sign_in endpoint with email and password parameters in order to login and get our auth token, so we’ll do just that:
Response body
Response headers
It’s alive! (Paw Print of the request and response: https://paw.pt/b4LiggBt)
The server will return the user data in response body, and the auth token in response headers (acess-token key)
Alternatively you can use this curl command to test it in the terminal:
curl -v -X "POST" "http://localhost:3000/auth/sign_in" \
-H "Content-Type: application/json; charset=utf-8" \
-d $'{
"email": "[email protected]",
"password": "monkey67"
}'
So now that our backend have the minimal functionality, let’s attack the frontend.