module GrapeDeviseAuth::Concerns::User
Public Class Methods
tokens_match?(token_hash, token)
click to toggle source
# File lib/grape_devise_auth/concerns/user.rb, line 8 def self.tokens_match?(token_hash, token) @token_equality_cache ||= {} key = "#{token_hash}/#{token}" result = @token_equality_cache[key] ||= (::BCrypt::Password.new(token_hash) == token) if @token_equality_cache.size > 10000 @token_equality_cache = {} end result end
Public Instance Methods
allow_password_change()
click to toggle source
# File lib/grape_devise_auth/concerns/user.rb, line 44 def allow_password_change @allow_password_change || false end
build_auth_header(token, client_id='default')
click to toggle source
# File lib/grape_devise_auth/concerns/user.rb, line 68 def build_auth_header(token, client_id='default') client_id ||= 'default' # client may use expiry to prevent validation request if expired # must be cast as string or headers will break expiry = self.tokens[client_id]['expiry'] || self.tokens[client_id][:expiry] max_clients = GrapeDeviseAuth.max_number_of_devices while self.tokens.keys.length > 0 and max_clients < self.tokens.keys.length oldest_token = self.tokens.min_by { |cid, v| v[:expiry] || v["expiry"] } self.tokens.delete(oldest_token.first) end self.save! return { GrapeDeviseAuth.headers_names[:"access-token"] => token, GrapeDeviseAuth.headers_names[:"token-type"] => "Bearer", GrapeDeviseAuth.headers_names[:"client"] => client_id, GrapeDeviseAuth.headers_names[:"expiry"] => expiry.to_s, GrapeDeviseAuth.headers_names[:"uid"] => self.uid } end
create_new_auth_token(client_id=nil)
click to toggle source
update user's auth token (should happen on each request)
# File lib/grape_devise_auth/concerns/user.rb, line 147 def create_new_auth_token(client_id=nil) client_id ||= SecureRandom.urlsafe_base64(nil, false) last_token ||= nil token = SecureRandom.urlsafe_base64(nil, false) token_hash = ::BCrypt::Password.create(token) expiry = (Time.now + GrapeDeviseAuth.token_lifespan).to_i if self.tokens[client_id] and self.tokens[client_id]['token'] last_token = self.tokens[client_id]['token'] end self.tokens[client_id] = { token: token_hash, expiry: expiry, last_token: last_token, updated_at: Time.now } return build_auth_header(token, client_id) end
email_changed?()
click to toggle source
# File lib/grape_devise_auth/concerns/user.rb, line 53 def email_changed? false end
email_required?()
click to toggle source
don't use default devise email validation
# File lib/grape_devise_auth/concerns/user.rb, line 49 def email_required? false end
extend_batch_buffer(token, client_id)
click to toggle source
# File lib/grape_devise_auth/concerns/user.rb, line 92 def extend_batch_buffer(token, client_id) self.tokens[client_id]['updated_at'] = Time.now return build_auth_header(token, client_id) end
token_can_be_reused?(token, client_id)
click to toggle source
allow batch requests to use the previous token
# File lib/grape_devise_auth/concerns/user.rb, line 128 def token_can_be_reused?(token, client_id) # ghetto HashWithIndifferentAccess updated_at = self.tokens[client_id]['updated_at'] || self.tokens[client_id][:updated_at] last_token = self.tokens[client_id]['last_token'] || self.tokens[client_id][:last_token] return true if ( # ensure that the last token and its creation time exist updated_at and last_token and # ensure that previous token falls within the batch buffer throttle time of the last request Time.parse(updated_at) > Time.now - GrapeDeviseAuth.batch_request_buffer_throttle and # ensure that the token is valid ::BCrypt::Password.new(last_token) == token ) end
token_is_current?(token, client_id)
click to toggle source
# File lib/grape_devise_auth/concerns/user.rb, line 110 def token_is_current?(token, client_id) # ghetto HashWithIndifferentAccess expiry = self.tokens[client_id]['expiry'] || self.tokens[client_id][:expiry] token_hash = self.tokens[client_id]['token'] || self.tokens[client_id][:token] return true if ( # ensure that expiry and token are set expiry and token and # ensure that the token has not yet expired DateTime.strptime(expiry.to_s, '%s') > Time.now and # ensure that the token is valid GrapeDeviseAuth::Concerns::User.tokens_match?(token_hash, token) ) end
valid_token?(token, client_id='default')
click to toggle source
# File lib/grape_devise_auth/concerns/user.rb, line 98 def valid_token?(token, client_id='default') client_id ||= 'default' return false unless self.tokens[client_id] return true if token_is_current?(token, client_id) return true if token_can_be_reused?(token, client_id) # return false if none of the above conditions are met return false end
Protected Instance Methods
destroy_expired_tokens()
click to toggle source
# File lib/grape_devise_auth/concerns/user.rb, line 174 def destroy_expired_tokens if self.tokens self.tokens.delete_if do |cid, v| expiry = v[:expiry] || v["expiry"] DateTime.strptime(expiry.to_s, '%s') < Time.now end end end
remove_tokens_after_password_reset()
click to toggle source
# File lib/grape_devise_auth/concerns/user.rb, line 183 def remove_tokens_after_password_reset there_is_more_than_one_token = self.tokens && self.tokens.keys.length > 1 should_remove_old_tokens = GrapeDeviseAuth.remove_tokens_after_password_reset && encrypted_password_changed? && there_is_more_than_one_token if should_remove_old_tokens latest_token = self.tokens.max_by { |cid, v| v[:expiry] || v["expiry"] } self.tokens = { latest_token.first => latest_token.last } end end
set_empty_token_hash()
click to toggle source
# File lib/grape_devise_auth/concerns/user.rb, line 170 def set_empty_token_hash self.tokens ||= {} if has_attribute?(:tokens) end