I am using token based authentication system for my APIs which is built based on Devise. The user is authorised to access the pages only when the email and mobile number linked with his/her account has been verified. If anyone of the above is not verified, the user gets the 403 access forbidden error message.
def authenticate_user!
unless current_user
authorization_failed_error
end
if current_user.confirmed_at.blank?
unconfirmed_email_error
elsif current_user.mobile_confirmed_at.blank?
unconfirmed_mobile_error
end
end
When an user with a unconfirmed email is trying to access a content through the API, error message is thrown. But if the same user tries for the second time, Devise redirects the user to the login view page.
Above scenarios occur alternatively every time.
current_user is set by the set_user_by_token function of the Devise, as below.
def set_user_by_token(mappings=nil)
rc = resource_class(:user)
# no default user defined
return unless rc
#gets the headers names, which was set in the initialize file
uid_name = 'Uid'
access_token_name = 'Access-Token'
client_name = 'Client'
expiry_name = 'Expiry'
# parse header for values necessary for authentication
uid = request.headers[uid_name] || params[uid_name]
@token ||= request.headers[access_token_name] || params[access_token_name]
@client_id ||= request.headers[client_name] || params[client_name]
# client_id isn't required, set to 'default' if absent
@client_id ||= 'default'
# check for an existing user, authenticated via warden/devise, if enabled
devise_warden_user = warden.user(rc.to_s.underscore.to_sym)
if devise_warden_user && devise_warden_user.tokens[@client_id].nil?
@used_auth_by_token = false
@resource = devise_warden_user
@resource.create_new_auth_token
end
# user has already been found and authenticated
return @resource if @resource and @resource.class == rc
# ensure we clear the client_id
if !@token
@client_id = nil
return
end
return false unless @token
# mitigate timing attacks by finding by uid instead of auth token
user = uid && rc.find_by_email(uid)
if user && user.valid_token?(@token, @client_id)
# sign_in with bypass: true will be deprecated in the next version of Devise
if self.respond_to? :bypass_sign_in
bypass_sign_in(user, scope: :user)
else
sign_in(:user, user, store: false, bypass: true)
end
@resource = user
return @resource
else
# zero all values previously set values
@client_id = nil
return @resource = nil
end
end
I am not sure why Devise(or Warden) behaves like this every second time. It would be of great help if someone could figure this out.