diff --git a/config/locales/en.yml b/config/locales/en.yml index e7445b31..ee239f36 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -11,7 +11,6 @@ en: already_authenticated: "You are already signed in." inactive: "Your account is not activated yet." invalid: "Invalid email or password." - invalid_token: "Invalid authentication token." locked: "Your account is locked." not_found_in_database: "Invalid email or password." timeout: "Your session expired. Please sign in again to continue." diff --git a/lib/devise/models.rb b/lib/devise/models.rb index 1beb1fab..9893062d 100644 --- a/lib/devise/models.rb +++ b/lib/devise/models.rb @@ -84,11 +84,6 @@ module Devise devise_modules_hook! do include Devise::Models::Authenticatable - if selected_modules.include?(:token_authenticatable) - ActiveSupport::Deprecation.warn "devise :token_authenticatable is deprecated. " \ - "Please check Devise 3.1 release notes for more information on how to upgrade." - end - selected_modules.each do |m| mod = Devise::Models.const_get(m.to_s.classify) diff --git a/lib/devise/models/authenticatable.rb b/lib/devise/models/authenticatable.rb index beee021c..c80350a8 100644 --- a/lib/devise/models/authenticatable.rb +++ b/lib/devise/models/authenticatable.rb @@ -29,9 +29,7 @@ module Devise # It also accepts an array specifying the strategies that should allow params authentication. # # * +skip_session_storage+: By default Devise will store the user in session. - # You can skip storage for http and token auth by appending values to array: - # :skip_session_storage => [:token_auth] or :skip_session_storage => [:http_auth, :token_auth], - # by default is set to :skip_session_storage => [:http_auth]. + # By default is set to :skip_session_storage => [:http_auth]. # # == active_for_authentication? # diff --git a/lib/devise/models/token_authenticatable.rb b/lib/devise/models/token_authenticatable.rb deleted file mode 100644 index b9f1c189..00000000 --- a/lib/devise/models/token_authenticatable.rb +++ /dev/null @@ -1,92 +0,0 @@ -require 'devise/strategies/token_authenticatable' - -module Devise - module Models - # The TokenAuthenticatable module is responsible for generating an authentication token and - # validating the authenticity of the same while signing in. - # - # This module only provides a few helpers to help you manage the token, but it is up to you - # to choose how to use it. For example, if you want to have a new token every time the user - # saves his account, you can do the following: - # - # before_save :reset_authentication_token - # - # On the other hand, if you want to generate token unless one exists, you should use instead: - # - # before_save :ensure_authentication_token - # - # If you want to delete the token after it is used, you can do so in the - # after_token_authentication callback. - # - # == APIs - # - # If you are using token authentication with APIs and using trackable. Every - # request will be considered as a new sign in (since there is no session in - # APIs). You can disable this by creating a before filter as follow: - # - # before_filter :skip_trackable - # - # def skip_trackable - # request.env['devise.skip_trackable'] = true - # end - # - # == Options - # - # TokenAuthenticatable adds the following options to devise_for: - # - # * +token_authentication_key+: Defines name of the authentication token params key. E.g. /users/sign_in?some_key=... - # - module TokenAuthenticatable - extend ActiveSupport::Concern - - def self.required_fields(klass) - [:authentication_token] - end - - # Generate new authentication token (a.k.a. "single access token"). - def reset_authentication_token - self.authentication_token = self.class.authentication_token - end - - # Generate new authentication token and save the record. - def reset_authentication_token! - reset_authentication_token - save(:validate => false) - end - - # Generate authentication token unless already exists. - def ensure_authentication_token - reset_authentication_token if authentication_token.blank? - end - - # Generate authentication token unless already exists and save the record. - def ensure_authentication_token! - reset_authentication_token! if authentication_token.blank? - end - - # Hook called after token authentication. - def after_token_authentication - end - - def expire_auth_token_on_timeout - self.class.expire_auth_token_on_timeout - end - - module ClassMethods - def find_for_token_authentication(conditions) - find_for_authentication(:authentication_token => conditions[token_authentication_key]) - end - - # Generate a token checking if one does not already exist in the database. - def authentication_token - loop do - token = Devise.friendly_token - break token unless to_adapter.find_first({ :authentication_token => token }) - end - end - - Devise::Models.config(self, :token_authentication_key, :expire_auth_token_on_timeout) - end - end - end -end diff --git a/lib/devise/modules.rb b/lib/devise/modules.rb index 1c620ca3..a93b4b03 100644 --- a/lib/devise/modules.rb +++ b/lib/devise/modules.rb @@ -5,7 +5,6 @@ Devise.with_options :model => true do |d| d.with_options :strategy => true do |s| routes = [nil, :new, :destroy] s.add_module :database_authenticatable, :controller => :sessions, :route => { :session => routes } - s.add_module :token_authenticatable, :controller => :sessions, :route => { :session => routes }, :no_input => true s.add_module :rememberable, :no_input => true end diff --git a/lib/devise/strategies/token_authenticatable.rb b/lib/devise/strategies/token_authenticatable.rb deleted file mode 100644 index f4f9bddf..00000000 --- a/lib/devise/strategies/token_authenticatable.rb +++ /dev/null @@ -1,91 +0,0 @@ -require 'devise/strategies/base' - -module Devise - module Strategies - # 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 headers, you can use basic authentication passing the token as username and - # blank password. Since some clients may require a password, you can pass "X" as - # password and it will simply be ignored. - # - # You may also pass the token using the Token authentication mechanism provided - # by Rails: http://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html - # The token options are stored in request.env['devise.token_options'] - class TokenAuthenticatable < Authenticatable - def store? - super && !mapping.to.skip_session_storage.include?(:token_auth) - end - - def valid? - super || valid_for_token_auth? - end - - def authenticate! - resource = mapping.to.find_for_token_authentication(authentication_hash) - return fail(:invalid_token) unless resource - - if validate(resource) - resource.after_token_authentication - success!(resource) - end - end - - private - - # Token Authenticatable can be authenticated with params in any controller and any verb. - def valid_params_request? - true - end - - # Do not use remember_me behavior with token. - def remember_me? - false - end - - # Check if the model accepts this strategy as token authenticatable. - def token_authenticatable? - mapping.to.http_authenticatable?(:token_options) - end - - # Check if this is strategy is valid for token authentication by: - # - # * Validating if the model allows http token authentication; - # * If the http auth token exists; - # * If all authentication keys are present; - # - def valid_for_token_auth? - token_authenticatable? && auth_token.present? && with_authentication_hash(:token_auth, token_auth_hash) - end - - # Extract the auth token from the request - def auth_token - @auth_token ||= ActionController::HttpAuthentication::Token.token_and_options(request) - end - - # Extract a hash with attributes:values from the auth_token - def token_auth_hash - request.env['devise.token_options'] = auth_token.last - { authentication_keys.first => auth_token.first } - end - - # Try both scoped and non scoped keys - def params_auth_hash - if params[scope].kind_of?(Hash) && params[scope].has_key?(authentication_keys.first) - params[scope] - else - params - end - end - - # Overwrite authentication keys to use token_authentication_key. - def authentication_keys - @authentication_keys ||= [mapping.to.token_authentication_key] - end - end - end -end - -Warden::Strategies.add(:token_authenticatable, Devise::Strategies::TokenAuthenticatable) diff --git a/lib/generators/mongoid/devise_generator.rb b/lib/generators/mongoid/devise_generator.rb index 247bfcde..33b82bf8 100644 --- a/lib/generators/mongoid/devise_generator.rb +++ b/lib/generators/mongoid/devise_generator.rb @@ -47,9 +47,6 @@ module Mongoid # field :failed_attempts, :type => Integer, :default => 0 # Only if lock strategy is :failed_attempts # field :unlock_token, :type => String # Only if unlock strategy is :email or :both # field :locked_at, :type => Time - - ## Token authenticatable - # field :authentication_token, :type => String RUBY end end diff --git a/lib/generators/templates/devise.rb b/lib/generators/templates/devise.rb index 4b9f7ef9..fde9c14c 100644 --- a/lib/generators/templates/devise.rb +++ b/lib/generators/templates/devise.rb @@ -56,12 +56,9 @@ Devise.setup do |config| # Tell if authentication through HTTP Auth is enabled. False by default. # It can be set to an array that will enable http authentication only for the - # given strategies, for example, `config.http_authenticatable = [:token]` will - # enable it only for token authentication. The supported strategies are: + # given strategies, for example, `config.http_authenticatable = [:database]` will + # enable it only for database authentication. The supported strategies are: # :database = Support basic authentication with authentication key + password - # :token = Support basic authentication with token authentication key - # :token_options = Support token authentication with options as defined in - # http://api.rubyonrails.org/classes/ActionController/HttpAuthentication/Token.html # config.http_authenticatable = false # If http headers should be returned for AJAX requests. True by default. @@ -76,7 +73,7 @@ Devise.setup do |config| # config.paranoid = true # By default Devise will store the user in session. You can skip storage for - # :http_auth and :token_auth by adding those symbols to the array below. + # particular strategies by setting this option. # Notice that if you are skipping storage for all authentication paths, you # may want to disable generating routes to Devise's sessions controller by # passing :skip => :sessions to `devise_for` in your config/routes.rb @@ -196,10 +193,6 @@ Devise.setup do |config| # Require the `devise-encryptable` gem when using anything other than bcrypt # config.encryptor = :sha512 - # ==> Configuration for :token_authenticatable - # Defines name of the authentication token params key - # config.token_authentication_key = :auth_token - # ==> Scopes configuration # Turn scoped views on. Before rendering "sessions/new", it will first check for # "users/sessions/new". It's turned off by default because it's slower if you diff --git a/test/controllers/internal_helpers_test.rb b/test/controllers/internal_helpers_test.rb index cf38d136..59dc7a59 100644 --- a/test/controllers/internal_helpers_test.rb +++ b/test/controllers/internal_helpers_test.rb @@ -55,7 +55,7 @@ class HelpersTest < ActionController::TestCase end test 'require no authentication tests current mapping' do - @mock_warden.expects(:authenticate?).with(:rememberable, :token_authenticatable, :scope => :user).returns(true) + @mock_warden.expects(:authenticate?).with(:rememberable, :scope => :user).returns(true) @mock_warden.expects(:user).with(:user).returns(User.new) @controller.expects(:redirect_to).with(root_path) @controller.send :require_no_authentication @@ -71,7 +71,7 @@ class HelpersTest < ActionController::TestCase end test 'require no authentication sets a flash message' do - @mock_warden.expects(:authenticate?).with(:rememberable, :token_authenticatable, :scope => :user).returns(true) + @mock_warden.expects(:authenticate?).with(:rememberable, :scope => :user).returns(true) @mock_warden.expects(:user).with(:user).returns(User.new) @controller.expects(:redirect_to).with(root_path) @controller.send :require_no_authentication diff --git a/test/integration/http_authenticatable_test.rb b/test/integration/http_authenticatable_test.rb index 31dc3729..1934f311 100644 --- a/test/integration/http_authenticatable_test.rb +++ b/test/integration/http_authenticatable_test.rb @@ -88,16 +88,6 @@ class HttpAuthenticationTest < ActionDispatch::IntegrationTest end end - test 'sign in should authenticate with really long token' do - token = "token_containing_so_many_characters_that_the_base64_encoding_will_wrap" - user = create_user - user.update_attribute :authentication_token, token - get users_path(:format => :xml), {}, "HTTP_AUTHORIZATION" => "Basic #{Base64.encode64("#{token}:x")}" - assert_response :success - assert_match "user@test.com", response.body - assert warden.authenticated?(:user) - end - private def sign_in_as_new_user_with_http(username="user@test.com", password="12345678") diff --git a/test/integration/token_authenticatable_test.rb b/test/integration/token_authenticatable_test.rb deleted file mode 100644 index 38750fcf..00000000 --- a/test/integration/token_authenticatable_test.rb +++ /dev/null @@ -1,205 +0,0 @@ -require 'test_helper' - -class TokenAuthenticationTest < ActionDispatch::IntegrationTest - - test 'authenticate with valid authentication token key and value through params' do - swap Devise, :token_authentication_key => :secret_token do - sign_in_as_new_user_with_token - - assert_response :success - assert_current_url "/users?secret_token=#{VALID_AUTHENTICATION_TOKEN}" - assert_contain 'Welcome' - assert warden.authenticated?(:user) - end - end - - test 'authenticate with valid authentication token key and value through params, when params with the same key as scope exist' do - swap Devise, :token_authentication_key => :secret_token do - user = create_user_with_authentication_token - post exhibit_user_path(user), Devise.token_authentication_key => user.authentication_token, :user => { :some => "data" } - - assert_response :success - assert_contain 'User is authenticated' - assert warden.authenticated?(:user) - end - end - - test 'authenticate with valid authentication token key but does not store if stateless' do - swap Devise, :token_authentication_key => :secret_token, :skip_session_storage => [:token_auth] do - sign_in_as_new_user_with_token - assert warden.authenticated?(:user) - - get users_path - assert_redirected_to new_user_session_path - assert_not warden.authenticated?(:user) - end - end - - test 'authenticate with valid authentication token key and value through http' do - swap Devise, :token_authentication_key => :secret_token do - sign_in_as_new_user_with_token(:http_auth => true) - - assert_response :success - assert_match 'user@test.com', response.body - assert warden.authenticated?(:user) - end - end - - test 'does authenticate with valid authentication token key and value through params if not configured' do - swap Devise, :token_authentication_key => :secret_token, :params_authenticatable => [:database] do - sign_in_as_new_user_with_token - - assert_contain 'You need to sign in or sign up before continuing' - assert_contain 'Sign in' - assert_not warden.authenticated?(:user) - end - end - - test 'does authenticate with valid authentication token key and value through http if not configured' do - swap Devise, :token_authentication_key => :secret_token, :http_authenticatable => [:database] do - sign_in_as_new_user_with_token(:http_auth => true) - - assert_response 401 - assert_contain 'Invalid email or password.' - assert_not warden.authenticated?(:user) - end - end - - test 'does not authenticate with improper authentication token key' do - swap Devise, :token_authentication_key => :donald_duck_token do - sign_in_as_new_user_with_token(:auth_token_key => :secret_token) - assert_equal new_user_session_path, @request.path - - assert_contain 'You need to sign in or sign up before continuing' - assert_contain 'Sign in' - assert_not warden.authenticated?(:user) - end - end - - test 'does not authenticate with improper authentication token value' do - store_translations :en, :devise => {:failure => {:invalid_token => 'LOL, that was not a single character correct.'}} do - sign_in_as_new_user_with_token(:auth_token => '*** INVALID TOKEN ***') - assert_equal new_user_session_path, @request.path - - assert_contain 'LOL, that was not a single character correct.' - assert_contain 'Sign in' - assert_not warden.authenticated?(:user) - end - end - - test 'authenticate with valid authentication token key and do not store if stateless and timeoutable are enabled' do - swap Devise, :token_authentication_key => :secret_token, :skip_session_storage => [:token_auth], :timeout_in => (0.1).second do - user = sign_in_as_new_user_with_token - assert warden.authenticated?(:user) - - # Expiring does not work because we are setting the session value when accessing it - sleep 0.3 - - get_users_path_as_existing_user(user) - assert warden.authenticated?(:user) - end - end - - test 'should reset token and not authenticate when expire_auth_token_on_timeout is set to true, timeoutable is enabled and we have a timed out session' do - swap Devise, :token_authentication_key => :secret_token, :expire_auth_token_on_timeout => true, :timeout_in => (-1).minute do - user = sign_in_as_new_user_with_token - assert warden.authenticated?(:user) - token = user.authentication_token - - get_users_path_as_existing_user(user) - assert_not warden.authenticated?(:user) - user.reload - assert_not_equal token, user.authentication_token - end - end - - test 'should not be subject to injection' do - swap Devise, :token_authentication_key => :secret_token do - user1 = create_user_with_authentication_token() - - # Clean up user cache - @user = nil - - user2 = create_user_with_authentication_token(:email => "another@test.com") - user2.update_attribute(:authentication_token, "ANOTHERTOKEN") - - assert_not_equal user1, user2 - visit users_path(Devise.token_authentication_key.to_s + '[$ne]' => user1.authentication_token) - assert_nil warden.user(:user) - end - end - - test 'authenticate with valid authentication token key and value through http header' do - swap Devise, :token_authentication_key => :secret_token do - sign_in_as_new_user_with_token(:token_auth => true) - - assert_response :success - assert_match 'user@test.com', response.body - assert_equal request.env['devise.token_options'], {} - assert warden.authenticated?(:user) - end - end - - test 'authenticate with valid authentication token key and value through http header, with options' do - swap Devise, :token_authentication_key => :secret_token, :http_authenticatable => [:token_options] do - signature = "**TESTSIGNATURE**" - sign_in_as_new_user_with_token(:token_auth => true, :token_options => {:signature => signature, :nonce => 'def'}) - - assert_response :success - assert_match 'user@test.com', response.body - assert_equal request.env['devise.token_options'][:signature], signature - assert_equal request.env['devise.token_options'][:nonce], 'def' - assert warden.authenticated?(:user) - end - end - - test 'authenticate with valid authentication token key and value through http header without allowing token authorization setting is denied' do - swap Devise, :token_authentication_key => :secret_token, :http_authenticatable => false do - sign_in_as_new_user_with_token(:token_auth => true) - - assert_response :unauthorized - assert_nil warden.user(:user) - end - end - - test 'does not authenticate with improper authentication token value in header' do - sign_in_as_new_user_with_token(:token_auth => true, :auth_token => '*** INVALID TOKEN ***') - - assert_response :unauthorized - assert_nil warden.user(:user) - end - - private - - def sign_in_as_new_user_with_token(options = {}) - user = options.delete(:user) || create_user_with_authentication_token(options) - - options[:auth_token_key] ||= Devise.token_authentication_key - options[:auth_token] ||= user.authentication_token - - if options[:http_auth] - header = "Basic #{Base64.encode64("#{VALID_AUTHENTICATION_TOKEN}:X")}" - get users_path(:format => :xml), {}, "HTTP_AUTHORIZATION" => header - elsif options[:token_auth] - token_options = options[:token_options] || {} - header = ActionController::HttpAuthentication::Token.encode_credentials(options[:auth_token], token_options) - get users_path(:format => :xml), {}, "HTTP_AUTHORIZATION" => header - else - visit users_path(options[:auth_token_key].to_sym => options[:auth_token]) - end - - user - end - - def create_user_with_authentication_token(options={}) - user = create_user(options) - user.authentication_token = VALID_AUTHENTICATION_TOKEN - user.save - user - end - - def get_users_path_as_existing_user(user) - sign_in_as_new_user_with_token(:user => user) - end - -end diff --git a/test/mapping_test.rb b/test/mapping_test.rb index 77509476..1617d271 100644 --- a/test/mapping_test.rb +++ b/test/mapping_test.rb @@ -50,12 +50,12 @@ class MappingTest < ActiveSupport::TestCase end test 'has strategies depending on the model declaration' do - assert_equal [:rememberable, :token_authenticatable, :database_authenticatable], Devise.mappings[:user].strategies + assert_equal [:rememberable, :database_authenticatable], Devise.mappings[:user].strategies assert_equal [:database_authenticatable], Devise.mappings[:admin].strategies end test 'has no input strategies depending on the model declaration' do - assert_equal [:rememberable, :token_authenticatable], Devise.mappings[:user].no_input_strategies + assert_equal [:rememberable], Devise.mappings[:user].no_input_strategies assert_equal [], Devise.mappings[:admin].no_input_strategies end diff --git a/test/models/token_authenticatable_test.rb b/test/models/token_authenticatable_test.rb deleted file mode 100644 index 15da9dd4..00000000 --- a/test/models/token_authenticatable_test.rb +++ /dev/null @@ -1,55 +0,0 @@ -require 'test_helper' - -class TokenAuthenticatableTest < ActiveSupport::TestCase - - test 'should reset authentication token' do - user = new_user - user.reset_authentication_token - previous_token = user.authentication_token - user.reset_authentication_token - assert_not_equal previous_token, user.authentication_token - end - - test 'should ensure authentication token' do - user = new_user - user.ensure_authentication_token - previous_token = user.authentication_token - user.ensure_authentication_token - assert_equal previous_token, user.authentication_token - end - - test 'should authenticate a valid user with authentication token and return it' do - user = create_user - user.ensure_authentication_token! - user.confirm! - authenticated_user = User.find_for_token_authentication(:auth_token => user.authentication_token) - assert_equal authenticated_user, user - end - - test 'should return nil when authenticating an invalid user by authentication token' do - user = create_user - user.ensure_authentication_token! - user.confirm! - authenticated_user = User.find_for_token_authentication(:auth_token => user.authentication_token.reverse) - assert_nil authenticated_user - end - - test 'should not be subject to injection' do - user1 = create_user - user1.ensure_authentication_token! - user1.confirm! - - user2 = create_user - user2.ensure_authentication_token! - user2.confirm! - - user = User.find_for_token_authentication(:auth_token => {'$ne' => user1.authentication_token}) - assert_nil user - end - - test 'required_fields should contain the fields that Devise uses' do - assert_same_content Devise::Models::TokenAuthenticatable.required_fields(User), [ - :authentication_token - ] - end -end diff --git a/test/rails_app/app/mongoid/user.rb b/test/rails_app/app/mongoid/user.rb index a8adb9b7..867758ea 100644 --- a/test/rails_app/app/mongoid/user.rb +++ b/test/rails_app/app/mongoid/user.rb @@ -36,7 +36,4 @@ class User field :failed_attempts, :type => Integer, :default => 0 # Only if lock strategy is :failed_attempts field :unlock_token, :type => String # Only if unlock strategy is :email or :both field :locked_at, :type => Time - - ## Token authenticatable - field :authentication_token, :type => String end diff --git a/test/rails_app/db/migrate/20100401102949_create_tables.rb b/test/rails_app/db/migrate/20100401102949_create_tables.rb index 85e3000b..43bf8564 100644 --- a/test/rails_app/db/migrate/20100401102949_create_tables.rb +++ b/test/rails_app/db/migrate/20100401102949_create_tables.rb @@ -33,9 +33,6 @@ class CreateTables < ActiveRecord::Migration t.string :unlock_token # Only if unlock strategy is :email or :both t.datetime :locked_at - ## Token authenticatable - t.string :authentication_token - t.timestamps end diff --git a/test/rails_app/db/schema.rb b/test/rails_app/db/schema.rb index 8fae3c28..c36bc6fb 100644 --- a/test/rails_app/db/schema.rb +++ b/test/rails_app/db/schema.rb @@ -44,7 +44,6 @@ ActiveRecord::Schema.define(:version => 20100401102949) do t.integer "failed_attempts", :default => 0 t.string "unlock_token" t.datetime "locked_at" - t.string "authentication_token" t.datetime "created_at" t.datetime "updated_at" end diff --git a/test/rails_app/lib/shared_user.rb b/test/rails_app/lib/shared_user.rb index 9d7b34ef..511c23ca 100644 --- a/test/rails_app/lib/shared_user.rb +++ b/test/rails_app/lib/shared_user.rb @@ -3,7 +3,7 @@ module SharedUser included do devise :database_authenticatable, :confirmable, :lockable, :recoverable, - :registerable, :rememberable, :timeoutable, :token_authenticatable, + :registerable, :rememberable, :timeoutable, :trackable, :validatable, :omniauthable attr_accessor :other_key