Reputation: 1577
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.
Upvotes: 0
Views: 1783
Reputation: 1879
You can try out device-token-auth gem which built on top of devise for API authentications.
Please check documentation which describes the usage.
Upvotes: 0
Reputation: 11
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.
Upvotes: 1