-
Notifications
You must be signed in to change notification settings - Fork 11
Devise Server Setup
Eventually we want to create a gem to avoid repeating a lot of boilerplate. For now, you have to set it up yourself.
First, we'll use the devise-jwt gem.
# Gemfile
gem 'devise-jwt'Add the devise-jwt bits in the user model (or whatever your authenticated resource is). The gem supports various revocations strategies.
# /app/models/user.rb
class User < ApplicationRecord
devise :database_authenticatable, :registerable, :recoverable, :trackable, :validatable, :confirmable,
:jwt_authenticatable, jwt_revocation_strategy: Devise::JWT::RevocationStrategies::Null
def jwt_payload
{
user_id: id,
email: email,
firstName: first_name,
lastName: last_name
}
end
endNext, we might need to set a custom path in our routes to match the apiResourceName if it's not the same as your user model name.
# routes.rb
devise_for :users, path: :authNext, we need to change auth failure behavior:
# /app/controllers/custom_auth_failure.rb
class CustomAuthFailure < Devise::FailureApp
def respond
self.status = :unauthorized
self.content_type = :json
self.response_body = {errors: ['Invalid login credentials']}.to_json
end
endNext, we need to change the URLs in our emails to be the client side routes. (NOTE: Replace "user" and "users" if you set a different clientResourceName.)
# /app/helpers/users_mailer_helper.rb
module UsersMailerHelper
url_defaults = Rails.configuration.action_mailer.default_url_options
protocol = url_defaults[:protocol] || 'http'
port = ":#{url_defaults[:port]}" if url_defaults[:port].present?
Devise::URL_HELPERS.each do |module_name, actions|
actions.each do |action|
method = ['user', action, module_name, 'url'].compact.join '_'
path = ['users', module_name, action].compact.join '/'
define_method method do |params = nil|
query = "?#{params.map {|k,v| "#{k}=#{v}"}.join('&')}" if params.present?
"#{protocol}://#{url_defaults[:host]}#{port}/#{path}#{query}"
end
end
end
end# /app/mailers/users_mailer.rb
class UsersMailer < Devise::Mailer
helper :users_mailer # gives access to all helpers defined within `mailer_helper`.
default template_path: 'devise/mailer' # to make sure that your mailer uses the devise views
endNext, you need to edit your mailers to use the client side route helpers. For example:
<p>Welcome <%= @email %>!</p>
<p>You can confirm your account email through the link below:</p>
<p><%= link_to 'Confirm my account', user_confirmation_url(confirmation_token: @token) %></p># config/initializers/devise.rb
config.warden do |manager|
manager.failure_app = CustomAuthFailure
end
config.mailer = 'UsersMailer'
# Needs to not include :json or wildcards that match json.
config.navigational_formats = [:html]# config/application.rb
config.to_prepare do
DeviseController.respond_to :json
endIf you want to implement editing user profile with react-devise, you need to create a custome RegistrationController.
class RegistrationsController < Devise::RegistrationsController
def edit
respond_with resource
end
protected
def update_resource(resource, params)
if params[:password].present?
resource.update_with_password params
else
resource.update_without_password params
end
end
endThe overidden #update_resource method is only needed if you want to omit password fields. This is an existing limitation with Devise.
Modify the devise routes to use the custom controller.
# config/routes.rb
devise_for :users, path: :auth, controllers: {registrations: :registrations}If you update any user attributes that are included in your auth token (based on User#jwt_payload), then the current_user in your client will go stale. To fix that add the following to tell devise-jwt to send a fresh auth token.
# config/initializers/devise.rb
config.jwt do |jwt|
jwt.dispatch_requests = [
['PATCH', %r{^/auth$}]
]
end
# where 'auth' is the path for your devise routes.DISCLAIMER: You might want to consider NOT putting user related info into the token. Also, the code above does not revoke the old token, which also might NOT be a best practice. Please read this issue for more detail.