Reputation: 144
I am trying to develop a Rails app which will mostly communicate with Android app users. People should be able to sign up and sign in from within the app (they can only perform most requests when logged in).
I've looked through many tutorials on how to do the equivalent of "storing a session" on the app, but all of them recommend using the gem 'devise'
and its :token_authenticable
, which was deprecated for some time now.
I want serious advice on how to perform something equivalent. From my understanding, the client sends a request with params such as {email: "[email protected]", password: "pw12345"}
, I check that they match an existing user and retrieve a string token, which this client will from now on send in every request via headers (such as {"my_app_user_email": "[email protected]", "my_app_user_token":"abcdef123456"}
).
I've already set some methods with fake values, such as
def login
if valid_user?(params[:email], params[:password])
render json: {user_token: default_user_token}
else
render json: {message: 'Couldn\'t login'}, status: 401
end
end
Where default_user_token
is a fixed string, and valid_user?
compares with fixed values as well.
I guess my problem is knowing if this is the right approach, and if so, how can I make a User
model that creates and validates tokens?
Extra bit of code
def verify_token # This already works by using default values in the Android app code
email = request.headers['HTTP_MY_APP_USER_EMAIL']
token = request.headers['HTTP_MY_APP_USER_TOKEN']
user = User.find_by_email(email)
user && user.valid_token?(token) # returns true for default_user_token, for now
end
Upvotes: 4
Views: 1329
Reputation: 5770
I think your approach is correct. you can add an auth_token field to your model and generate a new one as the user logs in. you can generate the the token like that:
def generate_authentication_token!
begin
self.auth_token = Devise.friendly_token
end while self.class.exists?(auth_token: auth_token)
end
than you can create an authentication service that requires the user token to every request like that:
module Authenticable
#Devise overwritten method
def current_user
@current_user ||= User.find_by(auth_token: request.headers['Authorization'])
end
def authenticate_with_token!
render json: { errors: "Not authenticated" }, status: :unauthorized unless user_signed_in?
end
def user_signed_in?
current_user.present?
end
end
and then, you can create a SessionsController that log in/log out the user using devise:
def create
user_password = params[:session][:password]
user_email = params[:session][:email]
user = user_email.present? && User.find_by(email: user_email)
if user
if user.valid_password? user_password
sign_in user, store: false
user.generate_authentication_token!
user.save
render json: user, root: :user, status: 200, location: [:api, user]
else
render json: { errors: "Invalid email or password" }, status: 422
end
else
render json: { errors: "Invalid email or password" }, status: 422
end
end
def destroy
user = User.find_by(auth_token: params[:id])
user.generate_authentication_token!
user.save
head 204
end
I have this repo on github that uses this solution: https://github.com/brunojppb/api_on_rails
Upvotes: 1