Yes! But you will need to enable the support of separate routes for standard Devise. So do something like this:
DeviseTokenAuth.setup do |config|
config.enable_standard_devise_support = true
end
Rails.application.routes.draw do
# standard devise routes available at /users
# NOTE: make sure this comes first!!!
devise_for :users
# token auth routes available at /api/v1/auth
namespace :api do
scope :v1 do
mount_devise_token_auth_for 'User', at: 'auth'
end
end
end
Removing the new
routes will require significant modifications to devise. If the inclusion of the new
routes is causing your app any problems, post an issue in the issue tracker and it will be addressed ASAP.
I'm having trouble using this gem alongside ActiveAdmin...
For some odd reason, ActiveAdmin extends from your own app's ApplicationController
. This becomes a problem if you include the DeviseTokenAuth::Concerns::SetUserByToken
concern in your app's ApplicationController
.
The solution is to use two separate ApplicationController
classes - one for your API, and one for ActiveAdmin. Something like this:
# app/controllers/api_controller.rb
# API routes extend from this controller
class ApiController < ActionController::Base
include DeviseTokenAuth::Concerns::SetUserByToken
end
# app/controllers/application_controller.rb
# leave this for ActiveAdmin, and any other non-api routes
class ApplicationController < ActionController::Base
end
You may be interested in GrapeTokenAuth or GrapeDeviseTokenAuth.
This is the overall workflow for a User to reset their password:
-
user goes to a page on the front end site which contains a form with a single text field, they type their email address into this field and click a button to submit the form
-
that form submission sends a request to the API:
POST /auth/password
with some parameters:email
(the email supplied in the field) &redirect_url
(a page in the front end site that will contain a form withpassword
andpassword_confirmation
fields) -
the API responds to this request by generating a
reset_password_token
and sending an email (thereset_password_instructions.html.erb
file from devise) to the email address provided within theemail
parameter- we need to modify the
reset_password_instructions.html.erb
file to point to the API:GET /auth/password/edit
- for example, if you have your API under the
api/v1
namespaces:<%= link_to 'Change my password', edit_api_v1_user_password_url(reset_password_token: @token, config: message['client-config'].to_s, redirect_url: message['redirect-url'].to_s) %>
(I came up with thislink_to
by referring to this line)
- we need to modify the
-
the user clicks the link in the email, which brings them to the 'Verify user by password reset token' endpoint (
GET /password/edit
) -
this endpoint verifies the user and redirects them to the
redirect_url
(or the one you set in an initializer as default_password_reset_url) with the auth headers if they are who they claim to be (if theirreset_password_token
matches a User record) -
this
redirect_url
is a page on the frontend which contains apassword
andpassword_confirmation
field -
the user submits the form on this frontend page, which sends a request to API:
PUT /auth/password
with thepassword
andpassword_confirmation
parameters. In addition headers need to be included from the url params (you get these from the url as query params). A side note, ensure that the header names follow the convention outlined inconfig/initializers/devise_token_auth.rb
; at this time of writing it is:uid
,client
andaccess-token
.- Ensure that the
uid
sent in the headers is not URL-escaped. e.g. it should be [email protected], not bob%40example.com
- Ensure that the
-
the API changes the user's password and responds back with a success message
-
the front end needs to manually redirect the user to its login page after receiving this success response
-
the user logs in
The next diagram shows how it works:
If you get in any trouble configuring or overriding the behavior, you can check the issue #604.
- First, remove the migration generated by the following command
rails g devise_token_auth:install [USER_CLASS] [MOUNT_PATH]
and then:. - Create another fresh migration:
# create migration by running a command like this (where `User` is your USER_CLASS table):
# `rails g migration AddTokensToUsers provider:string uid:string tokens:text`
def up
add_column :users, :provider, :string, null: false, default: 'email'
add_column :users, :uid, :string, null: false, default: ''
add_column :users, :tokens, :text
# if your existing User model does not have an existing **encrypted_password** column uncomment below line.
# add_column :users, :encrypted_password, :null => false, :default => ""
# the following will update your models so that when you run your migration
# updates the user table immediately with the above defaults
User.reset_column_information
# finds all existing users and updates them.
# if you change the default values above you'll also have to change them here below:
User.find_each do |user|
user.uid = user.email
user.provider = 'email'
user.save!
end
# to speed up lookups to these columns:
add_index :users, [:uid, :provider], unique: true
end
def down
# if you added **encrypted_password** above, add here to successfully rollback
remove_columns :users, :provider, :uid, :tokens
end