I'm currently working on a whisky tasting and bottle inventory application, and have gotten to the point where I need to add some user security. I decided to try Devise, even though it's more focused on web application security than API security.
Devise previously supported authentication tokens, but removed TokenAuthenticatable
from their codebase in late 2013. Fortunately, devise_token_auth was developed, which adds token authentication to Devise by installing an additional gem.
Generate your code
After installing the devise_token_auth
gem, run this command to generate your /auth
routes and User
model:
rails generate devise_token_auth:install User auth
Run db:migrate
to write your changes to the database. I added the following code to my seeds.db
to go on and add a user to the users
table:
User.create({
uid: 'GenBurnside',
username: 'GenBurnside',
provider: 'email',
name: 'Matt Hughes',
email: 'matthughes.tech@gmail.com',
password: 'asdfasdfasdf',
password_confirmation: 'asdfasdfasdf'
})
Get an access token
With a user created, now we can try validating the credentials, and making an authorized API call.
POST /auth/sign_in
{
"email": "matthughes.tech@gmail.com",
"password": "asdfasdfasdf"
}
If you send the correct credentials, the response will contain the User
object, and several headers we need to make authorized requests.
Access-Token: NyUIH_nI3vsKkNFtu3hmOA
Client: R9H84prlUIrrYPXr4iKuDQ
Expiry: 1438378648
Token-Type: Bearer
UID: matthughes.tech@gmail.com
You can verify the access token by sending the headers above to the GET auth/validate_token
route.
Make an authorized request
You need to add before_action :authenticate_user!
to the controller you want to protect:
class ThingController < ApplicationController
before_action :authenticate_user!
def action
user_id = current_user.id
render :json => { message: "It worked!" }
end
end
This will require authentication for all actions in that controller. Other requests will return a 401 Unauthorized
.
Do another POST /auth/sign_in
to get the authentication headers you need, and send them to GET /thing
. You should get a 200 OK
response.
If you wait more than five seconds and use the same token again, you'll get a 401 Unauthorized
response. This is because devise_token_auth
issues a new token after every request by default. I disabled this feature while developing the API for simplicity. To disable, edit config/initializers/devise_token_auth.rb
, and add the following line:
config.change_headers_on_each_request = false
Accessing the user in the controller
current_user
will get the current Devise user for that request. current_user.id
is used in the example above. If you wanted to access their username or e-mail, you could use current_user.username
or current_user.email
.