Extending Devise Sessionscontroller to Authenticate Using JSON

Extending Devise SessionsController to authenticate using JSON

This is what finally worked.

class Api::V1::SessionsController < Devise::SessionsController  
def create
respond_to do |format|
format.html { super }
format.json {
warden.authenticate!(:scope => resource_name, :recall => "#{controller_path}#new")
render :status => 200, :json => { :error => "Success" }
}
end
end
def destroy
super
end
end

Also change routes.rb, remember the order is important.

devise_for :users, :controllers => { :sessions => "api/v1/sessions" }
devise_scope :user do
namespace :api do
namespace :v1 do
resources :sessions, :only => [:create, :destroy]
end
end
end

resources :users

Extending devise to support a JSON API

Ended up with creating a new stategy to warden.

On the valid? method i check if the params[:controller] comes from the api namespace.
This way I didn't touch the default devise authentication by http.

Using Devise 1.3 to authenticate JSON login requests

Perhaps this one > http://jessehowarth.com/devise?

I plan to do the same thing in a week or two.

Extending Devise to use remote login

I know this question is years old, but I also found the tutorial to be incomplete, and I recently spent a couple days trying to get remote authentication to work. So I will provide my solution in case it helps someone in the future. I am using Ruby 2.2.2 and Rails 4.2.5.1.

First of all, this gist was an EXTREMELY helpful reference for me.

I also used the gem fakeweb to mock API calls.

Here are what my files look like:

app/models/user.rb

class User < include ActiveModel::Model
# required because some before_validations are defined in devise
include ActiveModel::Validations
# required to define callbacks
extend ActiveModel::Callbacks
# taken from http://stackoverflow.com/questions/8936906/whats-the-correct-way-to-make-before-validation-etc-work-in-an-activemodel
include ActiveModel::Validations::Callbacks
extend Devise::Models

# create getter and setter methods internally for the fields below
attr_accessor :email, :auth_token

#required by Devise
define_model_callbacks :validation

devise :remote_authenticatable, :timeoutable

# Latest devise tries to initialize this class with values
# ignore it for now
def initialize(options={})
end
end

lib/models/remote_authenticatable.rb

require 'fakeweb' #used for mocking API calls

module Devise
module Models
module RemoteAuthenticatable
extend ActiveSupport::Concern

#
# Here you do the request to the external webservice
#
# If the authentication is successful you should return
# a resource instance
#
# If the authentication fails you should return false
#
def remote_authentication(authentication_hash)

FakeWeb.register_uri(:get, "http://localhost:3000/webservice/login.json",
:body => "{ \"success\": \"true\", \"auth_token\": \"secure_token_123\", \"email\": \"bob@1123.com\"}")

# Your logic to authenticate with the external webservice
response = Net::HTTP.get(URI.parse("http://localhost:3000/webservice/login.json"))

self.email = JSON.parse(response)["email"]
self.auth_token = JSON.parse(response)["auth_token"]
return self
end

module ClassMethods
####################################
# Overriden methods from Devise::Models::Authenticatable
####################################

#
# This method is called from:
# Warden::SessionSerializer in devise
#
# It takes as many params as elements had the array
# returned in serialize_into_session
#
# Recreates a resource from session data
#
def serialize_from_session(data, salt)
resource = self.new
resource.email = data['email']
resource.auth_token = data['auth_token']
resource
end

#
# Here you have to return and array with the data of your resource
# that you want to serialize into the session
#
# You might want to include some authentication data
#
def serialize_into_session(record)
[
{
:email => record.email,
:auth_token => record.auth_token
},
nil
]
end

end
end
end
end

config/initializers/remote_authenticatable.rb

module Devise
module Strategies
class RemoteAuthenticatable < Authenticatable
#
# For an example check : https://github.com/plataformatec/devise/blob/master/lib/devise/strategies/database_authenticatable.rb
#
# Method called by warden to authenticate a resource.
#
def authenticate!
#
# authentication_hash doesn't include the password
#
auth_params = authentication_hash
auth_params[:password] = password

#
# mapping.to is a wrapper over the resource model
#
resource = mapping.to.new

return fail! unless resource

# remote_authentication method is defined in Devise::Models::RemoteAuthenticatable
#
# validate is a method defined in Devise::Strategies::Authenticatable. It takes
#a block which must return a boolean value.
#
# If the block returns true the resource will be logged in
# If the block returns false the authentication will fail!
#
# resource = resource.remote_authentication(auth_params)
if validate(resource){ resource = resource.remote_authentication(auth_params) }
success!(resource)
end
end
end
end
end

config/initializers/devise.rb

Devise.setup do |config|
# ...
# ...
# OTHER CONFIGURATION CODE HERE
# ...
# ...

# ==> Warden configuration
# If you want to use other strategies, that are not supported by Devise, or
# change the failure app, you can configure them inside the config.warden block.
#
# config.warden do |manager|
# manager.intercept_401 = false
# manager.default_strategies(scope: :user).unshift :some_external_strategy
# end

# BEGIN code that was added to this file
config.warden do |manager|
manager.strategies.add(:remote_authenticatable, Devise::Strategies::RemoteAuthenticatable)
manager.default_strategies(:scope => :user).unshift :remote_authenticatable
end

Devise.add_module :remote_authenticatable, :controller => :sessions, :route => { :session => :routes }
# END code that was added to this file

# ...
# ...
# OTHER CONFIGURATION CODE HERE
# ...
# ...
end

config/application.rb

# ...
# ...
# OTHER CODE HERE
# ...
# ...

module RemoteAuth
class Application < Rails::Application
# ...
# OTHER CODE HERE
# ...

# BEGIN code that was added to this file
config.autoload_paths += Dir["#{config.root}/lib/**/"]
config.autoload_paths += Dir["#{config.root}/app/models/**/"]
# END code that was added to this file
end
end

How do I authenticate using Devise in a Rails REST API App?

After getting a few tips, I found a number of great sites. There are several ways to do this, however I don't know which one is best, but these sites help a long way:

  • https://github.com/lynndylanhurley/devise_token_auth (An extension to
    Devise)
  • https://labs.kollegorna.se/blog/2015/04/build-an-api-now/
    (Manual way)
  • Token based authentication for Rails JSON APIs (SO Question)
  • Rails API : Best way to implement authentication? (SO Question)
  • Rails API : Best way to implement authentication?

Rails: Unable to sign_in a user using Devise

A better and easy way to solve this problem is using devise with devise_token_auth gem to accomplish the JWT(JSON Web Token) authentication system. This gem will give you all the tools you need to create a JWS system. We had a talk about API Authentication in my town and I created a basic app using this feature. I will describe how I have implemented the solution here, but fell free to check it out the Sample Repo here. It is an Instagram-like app.

Step 1:
Add to your Gemfile:

gem 'devise_token_auth'

Install the gem

$ bundle install

step 2: generate the configurations needed by the gem

$ rails generate devise_token_auth:install User auth
$ bundle exec rake db:migrate

It will generate a migration file that adds a couple of new attributes to your user model, but basically the main line is that:

 ## Tokens
t.text :tokens

It will provide an array of tokens that will be kept in the database for user authentication

It also adds the routes for authentication/sign_in/sign_up

mount_devise_token_auth_for "User", at: 'auth'

step 3: lookup for the routes

Now, on your terminal, execute:

$ rake routes

It will show the routes for authentication, sign in and sign_up

step 4: Using postman to try it out

As you mentioned, using the Postman, you now can send a POST to localhost:3000/auth/ with email, password, and password_confirmation(those fields are required) to register the user. (Check the devise_token_auth README to see how to sign_in and sign_out).

The key here is that registration request and sign_in request will return the response with many headers(if the status code was OK) that contains all the authentication information you need. You have to extract those headers on your mobile client(or even using postman to test the authentication required routes) and add to all further requests that require authentication in your app.

Try to do that, clone and check the Sample Repo that I pointed out and see if you get it done. Let me know if you got it.



Related Topics



Leave a reply



Submit