I'm having a little trouble figuring out how to create a token based authentication system for a Rails API.
I'm planning to build an Ember app where users should be able to sign in by providing a password and username.
On the rails side I have setup a an AuthenticationToken model which checks the authorization token and non-persisted secret. I store an encrypted secret based on the secret and the AuthenticationToken id (a BSON::ObjectId).
Basically my idea is that client should be able to authenticate as long as it hangs onto the secret. And then it needs to request a new access token. I want to keep it stateless by avoiding sessions.
I'm very confused about what the route and controller should look like where the user trades his user/pass for an access token. As the numerous blog post/tutorial I have found omits that part. Do I just create a simple POST route where the client sends credentials and gets the token back as JSON? Or should I use HTTP BASIC AUTH?
class User
include Mongoid::Document
include ActiveModel::SecurePassword
embeds_many :tokens, class_name: 'Users::AuthenticationToken'
field :email, type: String
field :username, type: String
field :password_digest, type: String
validates :email, presence: true, uniqueness: true
has_secure_password
end
require "securerandom"
require "bcrypt"
class Users::AuthenticationToken
include Mongoid::Document
embedded_in :user
attr_accessor :secret
field :hashed_secret
validates :hashed_secret, uniqueness: true, presence: true
before_validation :generate_secret
def self.find_by_id(id)
begin
id = BSON::ObjectId.from_string(id) unless id.is_a?(BSON::ObjectId)
tokens = User.find_by('tokens._id' => id).try(:tokens)
tokens ? tokens.find_by(id: id) : nil
rescue Mongoid::Errors::DocumentNotFound, BSON::ObjectId::Invalid
nil
end
end
# @return [Boolean]
def has_secret? secret
BCrypt::Password.new(hashed_secret) == secret
end
def authenticate!(secret)
user if has_secret? secret
end
private
def generate_secret
self.secret = SecureRandom.urlsafe_base64(32)
self.hashed_secret = BCrypt::Password.create secret, cost: cost
end
def cost
Rails.env.test? ? 1 : 10
end
end
I also have a authenticate method which looks something like this:
def authenticate!
token = Users::AuthenticationToken.find_by_id(token_id)
user = token.try(:authenticate!, secret)
user.nil? ? fail!("Could not log in") : success!(user)
end
Aucun commentaire:
Enregistrer un commentaire