devise_token_auth 1.0.0 → 1.1.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/README.md +4 -2
- data/app/controllers/devise_token_auth/application_controller.rb +2 -3
- data/app/controllers/devise_token_auth/concerns/resource_finder.rb +11 -12
- data/app/controllers/devise_token_auth/concerns/set_user_by_token.rb +39 -55
- data/app/controllers/devise_token_auth/confirmations_controller.rb +63 -20
- data/app/controllers/devise_token_auth/omniauth_callbacks_controller.rb +77 -29
- data/app/controllers/devise_token_auth/passwords_controller.rb +44 -30
- data/app/controllers/devise_token_auth/registrations_controller.rb +33 -40
- data/app/controllers/devise_token_auth/sessions_controller.rb +5 -5
- data/app/controllers/devise_token_auth/unlocks_controller.rb +4 -4
- data/app/models/devise_token_auth/concerns/active_record_support.rb +16 -0
- data/app/models/devise_token_auth/concerns/confirmable_support.rb +27 -0
- data/app/models/devise_token_auth/concerns/mongoid_support.rb +19 -0
- data/app/models/devise_token_auth/concerns/tokens_serialization.rb +19 -0
- data/app/models/devise_token_auth/concerns/user.rb +52 -71
- data/app/models/devise_token_auth/concerns/user_omniauth_callbacks.rb +3 -3
- data/app/validators/{email_validator.rb → devise_token_auth_email_validator.rb} +1 -1
- data/config/locales/da-DK.yml +2 -0
- data/config/locales/de.yml +2 -0
- data/config/locales/en.yml +7 -0
- data/config/locales/es.yml +2 -0
- data/config/locales/fr.yml +2 -0
- data/config/locales/he.yml +52 -0
- data/config/locales/it.yml +2 -0
- data/config/locales/ja.yml +4 -2
- data/config/locales/ko.yml +51 -0
- data/config/locales/nl.yml +2 -0
- data/config/locales/pl.yml +6 -3
- data/config/locales/pt-BR.yml +2 -0
- data/config/locales/pt.yml +6 -3
- data/config/locales/ro.yml +2 -0
- data/config/locales/ru.yml +2 -0
- data/config/locales/sq.yml +2 -0
- data/config/locales/sv.yml +2 -0
- data/config/locales/uk.yml +2 -0
- data/config/locales/vi.yml +2 -0
- data/config/locales/zh-CN.yml +2 -0
- data/config/locales/zh-HK.yml +2 -0
- data/config/locales/zh-TW.yml +2 -0
- data/lib/devise_token_auth.rb +6 -3
- data/lib/devise_token_auth/blacklist.rb +2 -0
- data/lib/devise_token_auth/engine.rb +7 -1
- data/lib/devise_token_auth/rails/routes.rb +1 -1
- data/lib/devise_token_auth/token_factory.rb +126 -0
- data/lib/devise_token_auth/url.rb +3 -0
- data/lib/devise_token_auth/version.rb +1 -1
- data/lib/generators/devise_token_auth/USAGE +1 -1
- data/lib/generators/devise_token_auth/install_generator.rb +6 -90
- data/lib/generators/devise_token_auth/install_generator_helpers.rb +98 -0
- data/lib/generators/devise_token_auth/install_mongoid_generator.rb +46 -0
- data/lib/generators/devise_token_auth/templates/devise_token_auth.rb +10 -0
- data/lib/generators/devise_token_auth/templates/devise_token_auth_create_users.rb.erb +0 -7
- data/lib/generators/devise_token_auth/templates/user.rb.erb +2 -2
- data/lib/generators/devise_token_auth/templates/user_mongoid.rb.erb +56 -0
- data/test/controllers/custom/custom_confirmations_controller_test.rb +1 -1
- data/test/controllers/demo_user_controller_test.rb +2 -2
- data/test/controllers/devise_token_auth/confirmations_controller_test.rb +83 -19
- data/test/controllers/devise_token_auth/omniauth_callbacks_controller_test.rb +109 -42
- data/test/controllers/devise_token_auth/passwords_controller_test.rb +227 -102
- data/test/controllers/devise_token_auth/registrations_controller_test.rb +34 -7
- data/test/controllers/devise_token_auth/sessions_controller_test.rb +0 -38
- data/test/controllers/devise_token_auth/token_validations_controller_test.rb +2 -1
- data/test/dummy/app/active_record/confirmable_user.rb +11 -0
- data/test/dummy/app/{models → active_record}/lockable_user.rb +0 -0
- data/test/dummy/app/{models → active_record}/mang.rb +0 -0
- data/test/dummy/app/{models → active_record}/only_email_user.rb +0 -0
- data/test/dummy/app/{models → active_record}/scoped_user.rb +2 -2
- data/test/dummy/app/{models → active_record}/unconfirmable_user.rb +1 -2
- data/test/dummy/app/{models → active_record}/unregisterable_user.rb +3 -3
- data/test/dummy/app/active_record/user.rb +6 -0
- data/test/dummy/app/controllers/overrides/confirmations_controller.rb +3 -3
- data/test/dummy/app/controllers/overrides/passwords_controller.rb +3 -3
- data/test/dummy/app/controllers/overrides/registrations_controller.rb +1 -1
- data/test/dummy/app/controllers/overrides/sessions_controller.rb +2 -2
- data/test/dummy/app/models/{user.rb → concerns/favorite_color.rb} +7 -8
- data/test/dummy/app/mongoid/confirmable_user.rb +52 -0
- data/test/dummy/app/mongoid/lockable_user.rb +38 -0
- data/test/dummy/app/mongoid/mang.rb +46 -0
- data/test/dummy/app/mongoid/only_email_user.rb +33 -0
- data/test/dummy/app/mongoid/scoped_user.rb +50 -0
- data/test/dummy/app/mongoid/unconfirmable_user.rb +44 -0
- data/test/dummy/app/mongoid/unregisterable_user.rb +47 -0
- data/test/dummy/app/mongoid/user.rb +49 -0
- data/test/dummy/config/application.rb +23 -1
- data/test/dummy/config/boot.rb +4 -0
- data/test/dummy/config/initializers/devise.rb +285 -0
- data/test/dummy/config/initializers/devise_token_auth.rb +35 -4
- data/test/dummy/config/initializers/figaro.rb +1 -1
- data/test/dummy/config/initializers/omniauth.rb +1 -0
- data/test/dummy/config/routes.rb +2 -0
- data/test/dummy/db/migrate/20140715061447_devise_token_auth_create_users.rb +0 -7
- data/test/dummy/db/migrate/20140715061805_devise_token_auth_create_mangs.rb +0 -7
- data/test/dummy/db/migrate/20141222035835_devise_token_auth_create_only_email_users.rb +0 -7
- data/test/dummy/db/migrate/20141222053502_devise_token_auth_create_unregisterable_users.rb +0 -7
- data/test/dummy/db/migrate/20150708104536_devise_token_auth_create_unconfirmable_users.rb +0 -7
- data/test/dummy/db/migrate/20160103235141_devise_token_auth_create_scoped_users.rb +0 -7
- data/test/dummy/db/migrate/20160629184441_devise_token_auth_create_lockable_users.rb +0 -7
- data/test/dummy/db/migrate/20190924101113_devise_token_auth_create_confirmable_users.rb +49 -0
- data/test/dummy/db/schema.rb +26 -28
- data/test/factories/users.rb +3 -2
- data/test/lib/devise_token_auth/blacklist_test.rb +11 -0
- data/test/lib/devise_token_auth/token_factory_test.rb +191 -0
- data/test/lib/devise_token_auth/url_test.rb +2 -2
- data/test/lib/generators/devise_token_auth/install_generator_test.rb +51 -31
- data/test/lib/generators/devise_token_auth/install_generator_with_namespace_test.rb +51 -31
- data/test/models/concerns/mongoid_support_test.rb +31 -0
- data/test/models/concerns/tokens_serialization_test.rb +70 -0
- data/test/models/confirmable_user_test.rb +35 -0
- data/test/models/only_email_user_test.rb +0 -8
- data/test/models/user_test.rb +1 -33
- data/test/test_helper.rb +13 -3
- metadata +129 -26
- data/config/initializers/devise.rb +0 -198
| @@ -2,23 +2,13 @@ | |
| 2 2 |  | 
| 3 3 | 
             
            module DeviseTokenAuth
         | 
| 4 4 | 
             
              class PasswordsController < DeviseTokenAuth::ApplicationController
         | 
| 5 | 
            -
                before_action : | 
| 5 | 
            +
                before_action :validate_redirect_url_param, only: [:create, :edit]
         | 
| 6 6 | 
             
                skip_after_action :update_auth_header, only: [:create, :edit]
         | 
| 7 7 |  | 
| 8 | 
            -
                # this action is responsible for generating password reset tokens and
         | 
| 9 | 
            -
                # sending emails
         | 
| 8 | 
            +
                # this action is responsible for generating password reset tokens and sending emails
         | 
| 10 9 | 
             
                def create
         | 
| 11 10 | 
             
                  return render_create_error_missing_email unless resource_params[:email]
         | 
| 12 11 |  | 
| 13 | 
            -
                  # give redirect value from params priority
         | 
| 14 | 
            -
                  @redirect_url = params.fetch(
         | 
| 15 | 
            -
                    :redirect_url,
         | 
| 16 | 
            -
                    DeviseTokenAuth.default_password_reset_url
         | 
| 17 | 
            -
                  )
         | 
| 18 | 
            -
             | 
| 19 | 
            -
                  return render_create_error_missing_redirect_url unless @redirect_url
         | 
| 20 | 
            -
                  return render_create_error_not_allowed_redirect_url if blacklisted_redirect_url?
         | 
| 21 | 
            -
             | 
| 22 12 | 
             
                  @email = get_case_insensitive_field_from_resource_params(:email)
         | 
| 23 13 | 
             
                  @resource = find_resource(:uid, @email)
         | 
| 24 14 |  | 
| @@ -44,14 +34,13 @@ module DeviseTokenAuth | |
| 44 34 | 
             
                # this is where users arrive after visiting the password reset confirmation link
         | 
| 45 35 | 
             
                def edit
         | 
| 46 36 | 
             
                  # if a user is not found, return nil
         | 
| 47 | 
            -
                  @resource = with_reset_password_token(resource_params[:reset_password_token])
         | 
| 37 | 
            +
                  @resource = resource_class.with_reset_password_token(resource_params[:reset_password_token])
         | 
| 48 38 |  | 
| 49 39 | 
             
                  if @resource && @resource.reset_password_period_valid?
         | 
| 50 | 
            -
                     | 
| 40 | 
            +
                    token = @resource.create_token unless require_client_password_reset_token?
         | 
| 51 41 |  | 
| 52 42 | 
             
                    # ensure that user is confirmed
         | 
| 53 43 | 
             
                    @resource.skip_confirmation! if confirmable_enabled? && !@resource.confirmed_at
         | 
| 54 | 
            -
             | 
| 55 44 | 
             
                    # allow user to change password once without current_password
         | 
| 56 45 | 
             
                    @resource.allow_password_change = true if recoverable_enabled?
         | 
| 57 46 |  | 
| @@ -59,12 +48,16 @@ module DeviseTokenAuth | |
| 59 48 |  | 
| 60 49 | 
             
                    yield @resource if block_given?
         | 
| 61 50 |  | 
| 62 | 
            -
                     | 
| 63 | 
            -
             | 
| 64 | 
            -
             | 
| 65 | 
            -
             | 
| 66 | 
            -
             | 
| 67 | 
            -
             | 
| 51 | 
            +
                    if require_client_password_reset_token?
         | 
| 52 | 
            +
                      redirect_to DeviseTokenAuth::Url.generate(@redirect_url, reset_password_token: resource_params[:reset_password_token])
         | 
| 53 | 
            +
                    else
         | 
| 54 | 
            +
                      redirect_header_options = { reset_password: true }
         | 
| 55 | 
            +
                      redirect_headers = build_redirect_headers(token.token,
         | 
| 56 | 
            +
                                                                token.client,
         | 
| 57 | 
            +
                                                                redirect_header_options)
         | 
| 58 | 
            +
                      redirect_to(@resource.build_auth_url(@redirect_url,
         | 
| 59 | 
            +
                                                           redirect_headers))
         | 
| 60 | 
            +
                    end
         | 
| 68 61 | 
             
                  else
         | 
| 69 62 | 
             
                    render_edit_error
         | 
| 70 63 | 
             
                  end
         | 
| @@ -72,6 +65,15 @@ module DeviseTokenAuth | |
| 72 65 |  | 
| 73 66 | 
             
                def update
         | 
| 74 67 | 
             
                  # make sure user is authorized
         | 
| 68 | 
            +
                  if require_client_password_reset_token? && resource_params[:reset_password_token]
         | 
| 69 | 
            +
                    @resource = resource_class.with_reset_password_token(resource_params[:reset_password_token])
         | 
| 70 | 
            +
                    return render_update_error_unauthorized unless @resource
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                    @token = @resource.create_token
         | 
| 73 | 
            +
                  else
         | 
| 74 | 
            +
                    @resource = set_user_by_token
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
             | 
| 75 77 | 
             
                  return render_update_error_unauthorized unless @resource
         | 
| 76 78 |  | 
| 77 79 | 
             
                  # make sure account doesn't use oauth2 provider
         | 
| @@ -98,9 +100,9 @@ module DeviseTokenAuth | |
| 98 100 | 
             
                protected
         | 
| 99 101 |  | 
| 100 102 | 
             
                def resource_update_method
         | 
| 101 | 
            -
                  allow_password_change = recoverable_enabled? && @resource.allow_password_change == true
         | 
| 103 | 
            +
                  allow_password_change = recoverable_enabled? && @resource.allow_password_change == true || require_client_password_reset_token?
         | 
| 102 104 | 
             
                  if DeviseTokenAuth.check_current_password_before_update == false || allow_password_change
         | 
| 103 | 
            -
                    ' | 
| 105 | 
            +
                    'update'
         | 
| 104 106 | 
             
                  else
         | 
| 105 107 | 
             
                    'update_with_password'
         | 
| 106 108 | 
             
                  end
         | 
| @@ -114,7 +116,7 @@ module DeviseTokenAuth | |
| 114 116 | 
             
                  render_error(401, I18n.t('devise_token_auth.passwords.missing_redirect_url'))
         | 
| 115 117 | 
             
                end
         | 
| 116 118 |  | 
| 117 | 
            -
                def  | 
| 119 | 
            +
                def render_error_not_allowed_redirect_url
         | 
| 118 120 | 
             
                  response = {
         | 
| 119 121 | 
             
                    status: 'error',
         | 
| 120 122 | 
             
                    data:   resource_data
         | 
| @@ -178,15 +180,27 @@ module DeviseTokenAuth | |
| 178 180 | 
             
                  params.permit(*params_for_resource(:account_update))
         | 
| 179 181 | 
             
                end
         | 
| 180 182 |  | 
| 181 | 
            -
                def  | 
| 182 | 
            -
                   | 
| 183 | 
            +
                def render_not_found_error
         | 
| 184 | 
            +
                  render_error(404, I18n.t('devise_token_auth.passwords.user_not_found', email: @email))
         | 
| 185 | 
            +
                end
         | 
| 186 | 
            +
             | 
| 187 | 
            +
                def validate_redirect_url_param
         | 
| 188 | 
            +
                  # give redirect value from params priority
         | 
| 189 | 
            +
                  @redirect_url = params.fetch(
         | 
| 190 | 
            +
                    :redirect_url,
         | 
| 191 | 
            +
                    DeviseTokenAuth.default_password_reset_url
         | 
| 192 | 
            +
                  )
         | 
| 183 193 |  | 
| 184 | 
            -
                   | 
| 185 | 
            -
                   | 
| 194 | 
            +
                  return render_create_error_missing_redirect_url unless @redirect_url
         | 
| 195 | 
            +
                  return render_error_not_allowed_redirect_url if blacklisted_redirect_url?(@redirect_url)
         | 
| 186 196 | 
             
                end
         | 
| 187 197 |  | 
| 188 | 
            -
                def  | 
| 189 | 
            -
                   | 
| 198 | 
            +
                def reset_password_token_as_raw?(recoverable)
         | 
| 199 | 
            +
                  recoverable && recoverable.reset_password_token.present? && !require_client_password_reset_token?
         | 
| 200 | 
            +
                end
         | 
| 201 | 
            +
             | 
| 202 | 
            +
                def require_client_password_reset_token?
         | 
| 203 | 
            +
                  DeviseTokenAuth.require_client_password_reset_token
         | 
| 190 204 | 
             
                end
         | 
| 191 205 | 
             
              end
         | 
| 192 206 | 
             
            end
         | 
| @@ -28,42 +28,40 @@ module DeviseTokenAuth | |
| 28 28 | 
             
                  end
         | 
| 29 29 |  | 
| 30 30 | 
             
                  # if whitelist is set, validate redirect_url against whitelist
         | 
| 31 | 
            -
                  return render_create_error_redirect_url_not_allowed if blacklisted_redirect_url?
         | 
| 31 | 
            +
                  return render_create_error_redirect_url_not_allowed if blacklisted_redirect_url?(@redirect_url)
         | 
| 32 32 |  | 
| 33 | 
            -
                   | 
| 34 | 
            -
             | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 33 | 
            +
                  # override email confirmation, must be sent manually from ctrl
         | 
| 34 | 
            +
                  callback_name = defined?(ActiveRecord) && resource_class < ActiveRecord::Base ? :commit : :create
         | 
| 35 | 
            +
                  resource_class.set_callback(callback_name, :after, :send_on_create_confirmation_instructions)
         | 
| 36 | 
            +
                  resource_class.skip_callback(callback_name, :after, :send_on_create_confirmation_instructions)
         | 
| 37 37 |  | 
| 38 | 
            -
             | 
| 39 | 
            -
             | 
| 40 | 
            -
             | 
| 41 | 
            -
             | 
| 38 | 
            +
                  if @resource.respond_to? :skip_confirmation_notification!
         | 
| 39 | 
            +
                    # Fix duplicate e-mails by disabling Devise confirmation e-mail
         | 
| 40 | 
            +
                    @resource.skip_confirmation_notification!
         | 
| 41 | 
            +
                  end
         | 
| 42 42 |  | 
| 43 | 
            -
             | 
| 44 | 
            -
             | 
| 43 | 
            +
                  if @resource.save
         | 
| 44 | 
            +
                    yield @resource if block_given?
         | 
| 45 45 |  | 
| 46 | 
            -
             | 
| 47 | 
            -
             | 
| 48 | 
            -
             | 
| 49 | 
            -
                         | 
| 50 | 
            -
                         | 
| 51 | 
            -
                       | 
| 52 | 
            -
                        # user will require email authentication
         | 
| 53 | 
            -
                        @resource.send_confirmation_instructions(
         | 
| 54 | 
            -
                          client_config: params[:config_name],
         | 
| 55 | 
            -
                          redirect_url: @redirect_url
         | 
| 56 | 
            -
                        )
         | 
| 57 | 
            -
                      end
         | 
| 58 | 
            -
             | 
| 59 | 
            -
                      render_create_success
         | 
| 60 | 
            -
                    else
         | 
| 61 | 
            -
                      clean_up_passwords @resource
         | 
| 62 | 
            -
                      render_create_error
         | 
| 46 | 
            +
                    unless @resource.confirmed?
         | 
| 47 | 
            +
                      # user will require email authentication
         | 
| 48 | 
            +
                      @resource.send_confirmation_instructions({
         | 
| 49 | 
            +
                        client_config: params[:config_name],
         | 
| 50 | 
            +
                        redirect_url: @redirect_url
         | 
| 51 | 
            +
                      })
         | 
| 63 52 | 
             
                    end
         | 
| 64 | 
            -
             | 
| 53 | 
            +
             | 
| 54 | 
            +
                    if active_for_authentication?
         | 
| 55 | 
            +
                      # email auth has been bypassed, authenticate user
         | 
| 56 | 
            +
                      @token = @resource.create_token
         | 
| 57 | 
            +
                      @resource.save!
         | 
| 58 | 
            +
                      update_auth_header
         | 
| 59 | 
            +
                    end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                    render_create_success
         | 
| 62 | 
            +
                  else
         | 
| 65 63 | 
             
                    clean_up_passwords @resource
         | 
| 66 | 
            -
                     | 
| 64 | 
            +
                    render_create_error
         | 
| 67 65 | 
             
                  end
         | 
| 68 66 | 
             
                end
         | 
| 69 67 |  | 
| @@ -145,15 +143,6 @@ module DeviseTokenAuth | |
| 145 143 | 
             
                  }, status: 422
         | 
| 146 144 | 
             
                end
         | 
| 147 145 |  | 
| 148 | 
            -
                def render_create_error_email_already_exists
         | 
| 149 | 
            -
                  response = {
         | 
| 150 | 
            -
                    status: 'error',
         | 
| 151 | 
            -
                    data:   resource_data
         | 
| 152 | 
            -
                  }
         | 
| 153 | 
            -
                  message = I18n.t('devise_token_auth.registrations.email_already_exists', email: @resource.email)
         | 
| 154 | 
            -
                  render_error(422, message, response)
         | 
| 155 | 
            -
                end
         | 
| 156 | 
            -
             | 
| 157 146 | 
             
                def render_update_success
         | 
| 158 147 | 
             
                  render json: {
         | 
| 159 148 | 
             
                    status: 'success',
         | 
| @@ -193,7 +182,7 @@ module DeviseTokenAuth | |
| 193 182 | 
             
                  elsif account_update_params.key?(:current_password)
         | 
| 194 183 | 
             
                    'update_with_password'
         | 
| 195 184 | 
             
                  else
         | 
| 196 | 
            -
                    ' | 
| 185 | 
            +
                    'update'
         | 
| 197 186 | 
             
                  end
         | 
| 198 187 | 
             
                end
         | 
| 199 188 |  | 
| @@ -208,5 +197,9 @@ module DeviseTokenAuth | |
| 208 197 | 
             
                def validate_post_data which, message
         | 
| 209 198 | 
             
                  render_error(:unprocessable_entity, message, status: 'error') if which.empty?
         | 
| 210 199 | 
             
                end
         | 
| 200 | 
            +
             | 
| 201 | 
            +
                def active_for_authentication?
         | 
| 202 | 
            +
                  !@resource.respond_to?(:active_for_authentication?) || @resource.active_for_authentication?
         | 
| 203 | 
            +
                end
         | 
| 211 204 | 
             
              end
         | 
| 212 205 | 
             
            end
         | 
| @@ -26,7 +26,7 @@ module DeviseTokenAuth | |
| 26 26 | 
             
                    if (@resource.respond_to?(:valid_for_authentication?) && !@resource.valid_for_authentication? { valid_password }) || !valid_password
         | 
| 27 27 | 
             
                      return render_create_error_bad_credentials
         | 
| 28 28 | 
             
                    end
         | 
| 29 | 
            -
                    @ | 
| 29 | 
            +
                    @token = @resource.create_token
         | 
| 30 30 | 
             
                    @resource.save
         | 
| 31 31 |  | 
| 32 32 | 
             
                    sign_in(:user, @resource, store: false, bypass: false)
         | 
| @@ -48,11 +48,11 @@ module DeviseTokenAuth | |
| 48 48 | 
             
                def destroy
         | 
| 49 49 | 
             
                  # remove auth instance variables so that after_action does not run
         | 
| 50 50 | 
             
                  user = remove_instance_variable(:@resource) if @resource
         | 
| 51 | 
            -
                   | 
| 52 | 
            -
                   | 
| 51 | 
            +
                  client = @token.client if @token.client
         | 
| 52 | 
            +
                  @token.clear!
         | 
| 53 53 |  | 
| 54 | 
            -
                  if user &&  | 
| 55 | 
            -
                    user.tokens.delete( | 
| 54 | 
            +
                  if user && client && user.tokens[client]
         | 
| 55 | 
            +
                    user.tokens.delete(client)
         | 
| 56 56 | 
             
                    user.save!
         | 
| 57 57 |  | 
| 58 58 | 
             
                    yield user if block_given?
         | 
| @@ -34,14 +34,14 @@ module DeviseTokenAuth | |
| 34 34 | 
             
                def show
         | 
| 35 35 | 
             
                  @resource = resource_class.unlock_access_by_token(params[:unlock_token])
         | 
| 36 36 |  | 
| 37 | 
            -
                  if @resource | 
| 38 | 
            -
                     | 
| 37 | 
            +
                  if @resource.persisted?
         | 
| 38 | 
            +
                    token = @resource.create_token
         | 
| 39 39 | 
             
                    @resource.save!
         | 
| 40 40 | 
             
                    yield @resource if block_given?
         | 
| 41 41 |  | 
| 42 42 | 
             
                    redirect_header_options = { unlock: true }
         | 
| 43 | 
            -
                    redirect_headers = build_redirect_headers(token,
         | 
| 44 | 
            -
                                                               | 
| 43 | 
            +
                    redirect_headers = build_redirect_headers(token.token,
         | 
| 44 | 
            +
                                                              token.client,
         | 
| 45 45 | 
             
                                                              redirect_header_options)
         | 
| 46 46 | 
             
                    redirect_to(@resource.build_auth_url(after_unlock_path_for(@resource),
         | 
| 47 47 | 
             
                                                         redirect_headers))
         | 
| @@ -0,0 +1,16 @@ | |
| 1 | 
            +
            require_relative 'tokens_serialization'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module DeviseTokenAuth::Concerns::ActiveRecordSupport
         | 
| 4 | 
            +
              extend ActiveSupport::Concern
         | 
| 5 | 
            +
             | 
| 6 | 
            +
              included do
         | 
| 7 | 
            +
                serialize :tokens, DeviseTokenAuth::Concerns::TokensSerialization
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              class_methods do
         | 
| 11 | 
            +
                # It's abstract replacement .find_by
         | 
| 12 | 
            +
                def dta_find_by(attrs = {})
         | 
| 13 | 
            +
                  find_by(attrs)
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
              end
         | 
| 16 | 
            +
            end
         | 
| @@ -0,0 +1,27 @@ | |
| 1 | 
            +
            module DeviseTokenAuth::Concerns::ConfirmableSupport
         | 
| 2 | 
            +
              extend ActiveSupport::Concern
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              included do
         | 
| 5 | 
            +
                # Override standard devise `postpone_email_change?` method
         | 
| 6 | 
            +
                # for not to use `will_save_change_to_email?` & `email_changed?` methods.
         | 
| 7 | 
            +
                def postpone_email_change?
         | 
| 8 | 
            +
                  postpone = self.class.reconfirmable &&
         | 
| 9 | 
            +
                    email_value_in_database != email &&
         | 
| 10 | 
            +
                    !@bypass_confirmation_postpone &&
         | 
| 11 | 
            +
                    self.email.present? &&
         | 
| 12 | 
            +
                    (!@skip_reconfirmation_in_callback || !email_value_in_database.nil?)
         | 
| 13 | 
            +
                  @bypass_confirmation_postpone = false
         | 
| 14 | 
            +
                  postpone
         | 
| 15 | 
            +
                end
         | 
| 16 | 
            +
              end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
              protected
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              def email_value_in_database
         | 
| 21 | 
            +
                if Devise.rails51? && respond_to?(:email_in_database)
         | 
| 22 | 
            +
                  email_in_database
         | 
| 23 | 
            +
                else
         | 
| 24 | 
            +
                  email_was
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
              end
         | 
| 27 | 
            +
            end
         | 
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            module DeviseTokenAuth::Concerns::MongoidSupport
         | 
| 2 | 
            +
              extend ActiveSupport::Concern
         | 
| 3 | 
            +
             | 
| 4 | 
            +
              def as_json(options = {})
         | 
| 5 | 
            +
                options[:except] = (options[:except] || []) + [:_id]
         | 
| 6 | 
            +
                hash = super(options)
         | 
| 7 | 
            +
                hash['id'] = to_param
         | 
| 8 | 
            +
                hash
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              class_methods do
         | 
| 12 | 
            +
                # It's abstract replacement .find_by
         | 
| 13 | 
            +
                def dta_find_by(attrs = {})
         | 
| 14 | 
            +
                  find_by(attrs)
         | 
| 15 | 
            +
                rescue Mongoid::Errors::DocumentNotFound
         | 
| 16 | 
            +
                  nil
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            end
         | 
| @@ -0,0 +1,19 @@ | |
| 1 | 
            +
            module DeviseTokenAuth::Concerns::TokensSerialization
         | 
| 2 | 
            +
              # Serialization hash to json
         | 
| 3 | 
            +
              def self.dump(object)
         | 
| 4 | 
            +
                object.each_value(&:compact!) unless object.nil?
         | 
| 5 | 
            +
                JSON.generate(object)
         | 
| 6 | 
            +
              end
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              # Deserialization json to hash
         | 
| 9 | 
            +
              def self.load(json)
         | 
| 10 | 
            +
                case json
         | 
| 11 | 
            +
                when String
         | 
| 12 | 
            +
                  JSON.parse(json)
         | 
| 13 | 
            +
                when NilClass
         | 
| 14 | 
            +
                  {}
         | 
| 15 | 
            +
                else
         | 
| 16 | 
            +
                  json
         | 
| 17 | 
            +
                end
         | 
| 18 | 
            +
              end
         | 
| 19 | 
            +
            end
         | 
| @@ -1,7 +1,5 @@ | |
| 1 1 | 
             
            # frozen_string_literal: true
         | 
| 2 2 |  | 
| 3 | 
            -
            require 'bcrypt'
         | 
| 4 | 
            -
             | 
| 5 3 | 
             
            module DeviseTokenAuth::Concerns::User
         | 
| 6 4 | 
             
              extend ActiveSupport::Concern
         | 
| 7 5 |  | 
| @@ -9,7 +7,7 @@ module DeviseTokenAuth::Concerns::User | |
| 9 7 | 
             
                @token_equality_cache ||= {}
         | 
| 10 8 |  | 
| 11 9 | 
             
                key = "#{token_hash}/#{token}"
         | 
| 12 | 
            -
                result = @token_equality_cache[key] ||=  | 
| 10 | 
            +
                result = @token_equality_cache[key] ||= DeviseTokenAuth::TokenFactory.token_hash_is_token?(token_hash, token)
         | 
| 13 11 | 
             
                @token_equality_cache = {} if @token_equality_cache.size > 10000
         | 
| 14 12 | 
             
                result
         | 
| 15 13 | 
             
              end
         | 
| @@ -20,19 +18,21 @@ module DeviseTokenAuth::Concerns::User | |
| 20 18 | 
             
                  devise_modules.delete(:omniauthable)
         | 
| 21 19 | 
             
                else
         | 
| 22 20 | 
             
                  devise :database_authenticatable, :registerable,
         | 
| 23 | 
            -
                         :recoverable, : | 
| 21 | 
            +
                         :recoverable, :validatable, :confirmable
         | 
| 24 22 | 
             
                end
         | 
| 25 23 |  | 
| 26 | 
            -
                 | 
| 24 | 
            +
                if const_defined?('ActiveRecord') && ancestors.include?(ActiveRecord::Base)
         | 
| 25 | 
            +
                  include DeviseTokenAuth::Concerns::ActiveRecordSupport
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                if const_defined?('Mongoid') && ancestors.include?(Mongoid::Document)
         | 
| 29 | 
            +
                  include DeviseTokenAuth::Concerns::MongoidSupport
         | 
| 30 | 
            +
                end
         | 
| 27 31 |  | 
| 28 32 | 
             
                if DeviseTokenAuth.default_callbacks
         | 
| 29 33 | 
             
                  include DeviseTokenAuth::Concerns::UserOmniauthCallbacks
         | 
| 30 34 | 
             
                end
         | 
| 31 35 |  | 
| 32 | 
            -
                # can't set default on text fields in mysql, simulate here instead.
         | 
| 33 | 
            -
                after_save :set_empty_token_hash
         | 
| 34 | 
            -
                after_initialize :set_empty_token_hash
         | 
| 35 | 
            -
             | 
| 36 36 | 
             
                # get rid of dead tokens
         | 
| 37 37 | 
             
                before_save :destroy_expired_tokens
         | 
| 38 38 |  | 
| @@ -44,6 +44,10 @@ module DeviseTokenAuth::Concerns::User | |
| 44 44 | 
             
                def email_changed?; false; end
         | 
| 45 45 | 
             
                def will_save_change_to_email?; false; end
         | 
| 46 46 |  | 
| 47 | 
            +
                if DeviseTokenAuth.send_confirmation_email && devise_modules.include?(:confirmable)
         | 
| 48 | 
            +
                  include DeviseTokenAuth::Concerns::ConfirmableSupport
         | 
| 49 | 
            +
                end
         | 
| 50 | 
            +
             | 
| 47 51 | 
             
                def password_required?
         | 
| 48 52 | 
             
                  return false unless provider == 'email'
         | 
| 49 53 | 
             
                  super
         | 
| @@ -84,39 +88,25 @@ module DeviseTokenAuth::Concerns::User | |
| 84 88 | 
             
                  send_devise_notification(:unlock_instructions, raw, opts)
         | 
| 85 89 | 
             
                  raw
         | 
| 86 90 | 
             
                end
         | 
| 87 | 
            -
              end
         | 
| 88 91 |  | 
| 89 | 
            -
             | 
| 90 | 
            -
             | 
| 91 | 
            -
                token     ||= SecureRandom.urlsafe_base64(nil, false)
         | 
| 92 | 
            -
                expiry    ||= (Time.zone.now + token_lifespan).to_i
         | 
| 92 | 
            +
                def create_token(client: nil, lifespan: nil, cost: nil, **token_extras)
         | 
| 93 | 
            +
                  token = DeviseTokenAuth::TokenFactory.create(client: client, lifespan: lifespan, cost: cost)
         | 
| 93 94 |  | 
| 94 | 
            -
             | 
| 95 | 
            -
             | 
| 96 | 
            -
             | 
| 97 | 
            -
             | 
| 95 | 
            +
                  tokens[token.client] = {
         | 
| 96 | 
            +
                    token:  token.token_hash,
         | 
| 97 | 
            +
                    expiry: token.expiry
         | 
| 98 | 
            +
                  }.merge!(token_extras)
         | 
| 98 99 |  | 
| 99 | 
            -
             | 
| 100 | 
            +
                  clean_old_tokens
         | 
| 100 101 |  | 
| 101 | 
            -
             | 
| 102 | 
            -
              end
         | 
| 103 | 
            -
             | 
| 104 | 
            -
              module ClassMethods
         | 
| 105 | 
            -
                protected
         | 
| 106 | 
            -
             | 
| 107 | 
            -
                def tokens_has_json_column_type?
         | 
| 108 | 
            -
                  database_exists? && table_exists? && columns_hash['tokens'] && columns_hash['tokens'].type.in?([:json, :jsonb])
         | 
| 109 | 
            -
                end
         | 
| 110 | 
            -
             | 
| 111 | 
            -
                def database_exists?
         | 
| 112 | 
            -
                  ActiveRecord::Base.connection_pool.with_connection { |con| con.active? } rescue false
         | 
| 102 | 
            +
                  token
         | 
| 113 103 | 
             
                end
         | 
| 114 104 | 
             
              end
         | 
| 115 105 |  | 
| 116 | 
            -
              def valid_token?(token,  | 
| 117 | 
            -
                return false unless tokens[ | 
| 118 | 
            -
                return true if token_is_current?(token,  | 
| 119 | 
            -
                return true if token_can_be_reused?(token,  | 
| 106 | 
            +
              def valid_token?(token, client = 'default')
         | 
| 107 | 
            +
                return false unless tokens[client]
         | 
| 108 | 
            +
                return true if token_is_current?(token, client)
         | 
| 109 | 
            +
                return true if token_can_be_reused?(token, client)
         | 
| 120 110 |  | 
| 121 111 | 
             
                # return false if none of the above conditions are met
         | 
| 122 112 | 
             
                false
         | 
| @@ -126,10 +116,10 @@ module DeviseTokenAuth::Concerns::User | |
| 126 116 | 
             
              # can be passed on from the client
         | 
| 127 117 | 
             
              def send_confirmation_notification?; false; end
         | 
| 128 118 |  | 
| 129 | 
            -
              def token_is_current?(token,  | 
| 119 | 
            +
              def token_is_current?(token, client)
         | 
| 130 120 | 
             
                # ghetto HashWithIndifferentAccess
         | 
| 131 | 
            -
                expiry     = tokens[ | 
| 132 | 
            -
                token_hash = tokens[ | 
| 121 | 
            +
                expiry     = tokens[client]['expiry'] || tokens[client][:expiry]
         | 
| 122 | 
            +
                token_hash = tokens[client]['token'] || tokens[client][:token]
         | 
| 133 123 |  | 
| 134 124 | 
             
                return true if (
         | 
| 135 125 | 
             
                  # ensure that expiry and token are set
         | 
| @@ -144,53 +134,52 @@ module DeviseTokenAuth::Concerns::User | |
| 144 134 | 
             
              end
         | 
| 145 135 |  | 
| 146 136 | 
             
              # allow batch requests to use the previous token
         | 
| 147 | 
            -
              def token_can_be_reused?(token,  | 
| 137 | 
            +
              def token_can_be_reused?(token, client)
         | 
| 148 138 | 
             
                # ghetto HashWithIndifferentAccess
         | 
| 149 | 
            -
                updated_at = tokens[ | 
| 150 | 
            -
                 | 
| 139 | 
            +
                updated_at = tokens[client]['updated_at'] || tokens[client][:updated_at]
         | 
| 140 | 
            +
                last_token_hash = tokens[client]['last_token'] || tokens[client][:last_token]
         | 
| 151 141 |  | 
| 152 142 | 
             
                return true if (
         | 
| 153 143 | 
             
                  # ensure that the last token and its creation time exist
         | 
| 154 | 
            -
                  updated_at &&  | 
| 144 | 
            +
                  updated_at && last_token_hash &&
         | 
| 155 145 |  | 
| 156 146 | 
             
                  # ensure that previous token falls within the batch buffer throttle time of the last request
         | 
| 157 | 
            -
                   | 
| 147 | 
            +
                  updated_at.to_time > Time.zone.now - DeviseTokenAuth.batch_request_buffer_throttle &&
         | 
| 158 148 |  | 
| 159 149 | 
             
                  # ensure that the token is valid
         | 
| 160 | 
            -
                  :: | 
| 150 | 
            +
                  DeviseTokenAuth::TokenFactory.token_hash_is_token?(last_token_hash, token)
         | 
| 161 151 | 
             
                )
         | 
| 162 152 | 
             
              end
         | 
| 163 153 |  | 
| 164 154 | 
             
              # update user's auth token (should happen on each request)
         | 
| 165 | 
            -
              def create_new_auth_token( | 
| 155 | 
            +
              def create_new_auth_token(client = nil)
         | 
| 166 156 | 
             
                now = Time.zone.now
         | 
| 167 157 |  | 
| 168 | 
            -
                 | 
| 169 | 
            -
                   | 
| 170 | 
            -
                   | 
| 171 | 
            -
                   | 
| 172 | 
            -
                  updated_at: now
         | 
| 158 | 
            +
                token = create_token(
         | 
| 159 | 
            +
                  client: client,
         | 
| 160 | 
            +
                  last_token: tokens.fetch(client, {})['token'],
         | 
| 161 | 
            +
                  updated_at: now.to_s(:rfc822)
         | 
| 173 162 | 
             
                )
         | 
| 174 163 |  | 
| 175 | 
            -
                update_auth_header(token,  | 
| 164 | 
            +
                update_auth_header(token.token, token.client)
         | 
| 176 165 | 
             
              end
         | 
| 177 166 |  | 
| 178 | 
            -
              def build_auth_header(token,  | 
| 167 | 
            +
              def build_auth_header(token, client = 'default')
         | 
| 179 168 | 
             
                # client may use expiry to prevent validation request if expired
         | 
| 180 169 | 
             
                # must be cast as string or headers will break
         | 
| 181 | 
            -
                expiry = tokens[ | 
| 170 | 
            +
                expiry = tokens[client]['expiry'] || tokens[client][:expiry]
         | 
| 182 171 |  | 
| 183 172 | 
             
                {
         | 
| 184 173 | 
             
                  DeviseTokenAuth.headers_names[:"access-token"] => token,
         | 
| 185 174 | 
             
                  DeviseTokenAuth.headers_names[:"token-type"]   => 'Bearer',
         | 
| 186 | 
            -
                  DeviseTokenAuth.headers_names[:"client"]       =>  | 
| 175 | 
            +
                  DeviseTokenAuth.headers_names[:"client"]       => client,
         | 
| 187 176 | 
             
                  DeviseTokenAuth.headers_names[:"expiry"]       => expiry.to_s,
         | 
| 188 177 | 
             
                  DeviseTokenAuth.headers_names[:"uid"]          => uid
         | 
| 189 178 | 
             
                }
         | 
| 190 179 | 
             
              end
         | 
| 191 180 |  | 
| 192 | 
            -
              def update_auth_header(token,  | 
| 193 | 
            -
                headers = build_auth_header(token,  | 
| 181 | 
            +
              def update_auth_header(token, client = 'default')
         | 
| 182 | 
            +
                headers = build_auth_header(token, client)
         | 
| 194 183 | 
             
                clean_old_tokens
         | 
| 195 184 | 
             
                save!
         | 
| 196 185 |  | 
| @@ -204,9 +193,9 @@ module DeviseTokenAuth::Concerns::User | |
| 204 193 | 
             
                DeviseTokenAuth::Url.generate(base_url, args)
         | 
| 205 194 | 
             
              end
         | 
| 206 195 |  | 
| 207 | 
            -
              def extend_batch_buffer(token,  | 
| 208 | 
            -
                tokens[ | 
| 209 | 
            -
                update_auth_header(token,  | 
| 196 | 
            +
              def extend_batch_buffer(token, client)
         | 
| 197 | 
            +
                tokens[client]['updated_at'] = Time.zone.now.to_s(:rfc822)
         | 
| 198 | 
            +
                update_auth_header(token, client)
         | 
| 210 199 | 
             
              end
         | 
| 211 200 |  | 
| 212 201 | 
             
              def confirmed?
         | 
| @@ -217,16 +206,8 @@ module DeviseTokenAuth::Concerns::User | |
| 217 206 | 
             
                as_json(except: %i[tokens created_at updated_at])
         | 
| 218 207 | 
             
              end
         | 
| 219 208 |  | 
| 220 | 
            -
              def token_lifespan
         | 
| 221 | 
            -
                DeviseTokenAuth.token_lifespan
         | 
| 222 | 
            -
              end
         | 
| 223 | 
            -
             | 
| 224 209 | 
             
              protected
         | 
| 225 210 |  | 
| 226 | 
            -
              def set_empty_token_hash
         | 
| 227 | 
            -
                self.tokens ||= {} if has_attribute?(:tokens)
         | 
| 228 | 
            -
              end
         | 
| 229 | 
            -
             | 
| 230 211 | 
             
              def destroy_expired_tokens
         | 
| 231 212 | 
             
                if tokens
         | 
| 232 213 | 
             
                  tokens.delete_if do |cid, v|
         | 
| @@ -237,11 +218,11 @@ module DeviseTokenAuth::Concerns::User | |
| 237 218 | 
             
              end
         | 
| 238 219 |  | 
| 239 220 | 
             
              def should_remove_tokens_after_password_reset?
         | 
| 240 | 
            -
                if Rails::VERSION::MAJOR <= 5
         | 
| 221 | 
            +
                if Rails::VERSION::MAJOR <= 5 ||defined?('Mongoid')
         | 
| 241 222 | 
             
                  encrypted_password_changed? &&
         | 
| 242 223 | 
             
                    DeviseTokenAuth.remove_tokens_after_password_reset
         | 
| 243 224 | 
             
                else
         | 
| 244 | 
            -
                   | 
| 225 | 
            +
                  saved_change_to_attribute?(:encrypted_password) &&
         | 
| 245 226 | 
             
                    DeviseTokenAuth.remove_tokens_after_password_reset
         | 
| 246 227 | 
             
                end
         | 
| 247 228 | 
             
              end
         | 
| @@ -250,8 +231,8 @@ module DeviseTokenAuth::Concerns::User | |
| 250 231 | 
             
                return unless should_remove_tokens_after_password_reset?
         | 
| 251 232 |  | 
| 252 233 | 
             
                if tokens.present? && tokens.many?
         | 
| 253 | 
            -
                   | 
| 254 | 
            -
                  self.tokens = {  | 
| 234 | 
            +
                  client, token_data = tokens.max_by { |cid, v| v[:expiry] || v['expiry'] }
         | 
| 235 | 
            +
                  self.tokens = { client => token_data }
         | 
| 255 236 | 
             
                end
         | 
| 256 237 | 
             
              end
         | 
| 257 238 |  |