简体   繁体   中英

How to use Devise for authentication when using an API

I know there are other questions similar to this, but none of the answers worked under my circumstances. I'm trying to authenticate users through an API. More specifically, I have an iPhone app acting as the "client" and it sends posts requests to a sessions controller I have. Here it is:

class V1::SessionsController < ApplicationController

  def create
    @user = User.find_by_username(params[:username])

    if @user
      if @user.valid_password?(params[:password])
        sign_in(@user)
        render :create
      else 
        render_error_for @user
      end
    else
      render json: {
        status: 'ERROR',
        data: ['Incorrect Username']
      }, status: :unauthorized
    end
  end 
end

I read online that the sign_in(@user) should sign in the user, but that is not working as expected. Additionally, I read that you should reload the user before signing it in like this: @user.reload . This does not work either. I was wondering if maybe this has to do with the fact the I'm not inheriting from the Devise::SessionsController . I learned how to make a sessions controller like this online, so that might be the problem.

The way I'm testing this is by running user.last_sign_in_at in the rails console, but all I'm getting back is nil , which I'm pretty sure means that devise isn't successfully signing in the user. Any help is appreciated, thank you.

UPDATE

def create
    @user = User.find_by_username(params[:username])

    if @user
      if @user.valid_password?(params[:password])
        @user.last_sign_in_at = Time.now
        render :create
      else 
        render_error_for @user
      end
    else
      render json: {
        status: 'ERROR',
        data: ['Incorrect Username']
      }, status: :unauthorized
    end
  end

I have found a possible solution, but I haven't marked it as a solution because it seems to me that this solution isn't secure. I'm essentially substituting my own process for creating a session, (and assigning a DateTime to the last_sign_in_at field) in the place of Devise's process. Is this secure? Or is doing this a bad idea? Hard coding Devise's sessions#create action does not work for some reason. I speculate that this has to do with the fact that this is an API and not just a regular website.

As your said More specifically, I have an iPhone app acting as the "client" and it sends posts requests to a sessions controller I have

so I might think that the response you need will include user information and authentication token.

Try this way:

class SessionsController < Devise::SessionsController

  skip_before_action :verify_authenticity_token # skip CSRF check for APIs
  respond_to :json

  def create
    self.resource = warden.authenticate!(auth_options)
    set_flash_message!(:notice, :signed_in)
    sign_in(resource_name, resource)
    yield resource if block_given?
    respond_with(resource)
 end

 def respond_with(resource, opts = {})
   render json: resource.as_json(only: [:id,:email, :name, ... ])
                      .merge!({token: resource.token})
 end
end

Regards token, you can check :jwt_authenticatable, :jwt_revocation_strategy in devise gem.

So response will have user and token: user will be user information and token is authentication token for this user. Then you need store that token for next time when you call another request to backend, make sure you include it to header as Authorization.

In backend, we get current user based on the authentication token.

You can try out device-token-auth gem which built on top of devise for API authentications.

Please check documentation which describes the usage.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM