TokenAuthenticatable now works with HTTP Basic Auth by default (take a look at Highrise API for a good example). This basically allows you to pass the authentication token as HTTP Basic Auth username.

This commit is contained in:
José Valim
2010-04-01 19:09:33 +02:00
parent 2b5a068246
commit f5d01c217d
7 changed files with 131 additions and 42 deletions

View File

@@ -64,6 +64,10 @@ module Devise
mattr_accessor :http_authenticatable
@@http_authenticatable = true
# If params authenticatable is enabled by default.
mattr_accessor :params_authenticatable
@@params_authenticatable = true
# The realm used in Http Basic Authentication.
mattr_accessor :http_authentication_realm
@@http_authentication_realm = "Application"

View File

@@ -10,6 +10,10 @@ module Devise
# authentication_keys: parameters used for authentication. By default [:email].
#
# http_authenticatable: if this model allows http authentication. By default true.
# It also accepts an array specifying the strategies that should allow http.
#
# params_authenticatable: if this model allows authentication through request params. By default true.
# It also accepts an array specifying the strategies that should allow params authentication.
#
module Authenticatable
extend ActiveSupport::Concern
@@ -21,9 +25,17 @@ module Devise
end
module ClassMethods
Devise::Models.config(self, :authentication_keys, :http_authenticatable)
Devise::Models.config(self, :authentication_keys, :http_authenticatable, :params_authenticatable)
alias :http_authenticatable? :http_authenticatable
def params_authenticatable?(strategy)
params_authenticatable.is_a?(Array) ?
params_authenticatable.include?(strategy) : params_authenticatable
end
def http_authenticatable?(strategy)
http_authenticatable.is_a?(Array) ?
http_authenticatable.include?(strategy) : http_authenticatable
end
# Find first record based on conditions given (ie by the sign in form).
# Overwrite to add customized conditions, create a join, or maybe use a

View File

@@ -18,7 +18,8 @@ module Devise
# User.find(1).valid_authentication_token?('rI1t6PKQ8yP7VetgwdybB') # returns true/false
#
module TokenAuthenticatable
extend ActiveSupport::Concern
extend ActiveSupport::Concern
include Devise::Models::Authenticatable
included do
before_save :ensure_authentication_token

View File

@@ -26,45 +26,72 @@ module Devise
end
end
# Check if this is strategy is valid for http authentication.
def valid_for_http_auth?
mapping.to.http_authenticatable? && request.authorization && set_http_auth_hash
http_authenticatable? && request.authorization && with_authentication_hash(http_auth_hash)
end
# Check if this is strategy is valid for params authentication.
def valid_for_params_auth?
valid_controller? && valid_params? && set_params_auth_hash
params_authenticatable? && valid_controller? &&
valid_params? && with_authentication_hash(params_auth_hash)
end
# Check if the model accepts this strategy as http authenticatable.
def http_authenticatable?
mapping.to.http_authenticatable?(authenticatable_name)
end
# Check if the model accepts this strategy as params authenticatable.
def params_authenticatable?
mapping.to.params_authenticatable?(authenticatable_name)
end
# Extract the appropriate subhash for authentication from params.
def params_auth_hash
params[scope]
end
# Extract a hash with attributes:values from the http params.
def http_auth_hash
keys = [authentication_keys.first, :password]
Hash[*keys.zip(decode_credentials).flatten]
end
# Check if the controller is valid for params authentication.
def valid_controller?
mapping.controllers[:sessions] == params[:controller]
end
# Check if the params_auth_hash is valid for params authentication.
def valid_params?
params[scope].is_a?(Hash)
end
def set_http_auth_hash
keys = [authentication_keys.first, :password]
with_authentication_hash Hash[*keys.zip(decode_credentials).flatten]
params_auth_hash.is_a?(Hash)
end
# Helper to decode credentials from HTTP.
def decode_credentials
username_and_password = request.authorization.split(' ', 2).last || ''
ActiveSupport::Base64.decode64(username_and_password).split(/:/, 2)
end
def set_params_auth_hash
with_authentication_hash params[scope]
end
# Sets the authentication hash and the password from params_auth_hash or http_auth_hash.
def with_authentication_hash(hash)
self.authentication_hash = hash.slice(*authentication_keys)
self.password = hash[:password]
authentication_keys.all?{ |k| authentication_hash[k].present? } && password.present?
authentication_keys.all?{ |k| authentication_hash[k].present? }
end
# Holds the authentication keys.
def authentication_keys
@authentication_keys ||= mapping.to.authentication_keys
end
# Holds the authenticatable name for this class. Devise::Strategies::DatabaseAuthenticatable
# becomes simply :database.
def authenticatable_name
@authenticatable_name ||=
self.class.name.split("::").last.underscore.sub("_authenticatable", "").to_sym
end
end
end
end

View File

@@ -2,33 +2,42 @@ require 'devise/strategies/base'
module Devise
module Strategies
# Strategy for signing in a user, based on a authenticatable token.
# Redirects to sign_in page if it's not authenticated.
class TokenAuthenticatable < Base
def valid?
authentication_token(scope).present?
end
# Authenticate a user based on authenticatable token params, returning to warden
# success and the authenticated user if everything is okay. Otherwise redirect
# to sign in page.
# Strategy for signing in a user, based on a authenticatable token. This works for both params
# and http. For the former, all you need to do is to pass the params in the URL:
#
# http://myapp.example.com/?user_token=SECRET
#
# For HTTP, you can pass the token as username. Since some clients may require a password,
# you can pass anything and it will simply be ignored.
class TokenAuthenticatable < Authenticatable
def authenticate!
if resource = mapping.to.authenticate_with_token(params[scope] || params)
if resource = mapping.to.authenticate_with_token(authentication_hash)
success!(resource)
else
fail!(:invalid_token)
fail(:invalid_token)
end
end
private
# Detect authentication token in params: scoped or not.
def authentication_token(scope)
if params[scope]
params[scope][mapping.to.token_authentication_key]
else
params[mapping.to.token_authentication_key]
end
# TokenAuthenticatable params can be given to any controller.
def valid_controller?
true
end
# Do not use remember_me behavir with token.
def remember_me?
false
end
# Try both scoped and non scoped keys.
def params_auth_hash
params[scope] || params
end
# Overwrite authentication keys to use token_authentication_key.
def authentication_keys
@authentication_keys ||= [mapping.to.token_authentication_key]
end
end
end