diff options
author | Patrick Figel <patrick@figel.email> | 2017-04-15 13:26:03 +0200 |
---|---|---|
committer | Eugen <eugen@zeonfederated.com> | 2017-04-15 13:26:03 +0200 |
commit | df4ff9a8e13d776e1670c232655db0275a353a0f (patch) | |
tree | a7bdb4c0240e169bac01bf67b76f685e9a9b4a67 /spec | |
parent | 67ad84b7ebf080d6a6cbcb7d299e02c2a51d955e (diff) |
Add recovery code support for two-factor auth (#1773)
* Add recovery code support for two-factor auth When users enable two-factor auth, the app now generates ten single-use recovery codes. Users are encouraged to print the codes and store them in a safe place. The two-factor prompt during login now accepts both OTP codes and recovery codes. The two-factor settings UI allows users to regenerated lost recovery codes. Users who have set up two-factor auth prior to this feature being added can use it to generate recovery codes for the first time. Fixes #563 and fixes #987 * Set OTP_SECRET in test enviroment * add missing .html to view file names
Diffstat (limited to 'spec')
-rw-r--r-- | spec/controllers/auth/sessions_controller_spec.rb | 93 | ||||
-rw-r--r-- | spec/models/user_spec.rb | 3 |
2 files changed, 87 insertions, 9 deletions
diff --git a/spec/controllers/auth/sessions_controller_spec.rb b/spec/controllers/auth/sessions_controller_spec.rb index c2a1fbe91..0ec0b8f2f 100644 --- a/spec/controllers/auth/sessions_controller_spec.rb +++ b/spec/controllers/auth/sessions_controller_spec.rb @@ -5,7 +5,7 @@ RSpec.describe Auth::SessionsController, type: :controller do describe 'GET #new' do before do - request.env["devise.mapping"] = Devise.mappings[:user] + request.env['devise.mapping'] = Devise.mappings[:user] end it 'returns http success' do @@ -15,19 +15,94 @@ RSpec.describe Auth::SessionsController, type: :controller do end describe 'POST #create' do - let(:user) { Fabricate(:user, email: 'foo@bar.com', password: 'abcdefgh') } - before do - request.env["devise.mapping"] = Devise.mappings[:user] - post :create, params: { user: { email: user.email, password: user.password } } + request.env['devise.mapping'] = Devise.mappings[:user] end - it 'redirects to home' do - expect(response).to redirect_to(root_path) + context 'using password authentication' do + let(:user) { Fabricate(:user, email: 'foo@bar.com', password: 'abcdefgh') } + + context 'using a valid password' do + before do + post :create, params: { user: { email: user.email, password: user.password } } + end + + it 'redirects to home' do + expect(response).to redirect_to(root_path) + end + + it 'logs the user in' do + expect(controller.current_user).to eq user + end + end + + context 'using an invalid password' do + before do + post :create, params: { user: { email: user.email, password: 'wrongpw' } } + end + + it 'shows a login error' do + expect(flash[:alert]).to match I18n.t('devise.failure.invalid', authentication_keys: 'Email') + end + + it "doesn't log the user in" do + expect(controller.current_user).to be_nil + end + end end - it 'logs the user in' do - expect(controller.current_user).to eq user + context 'using two-factor authentication' do + let(:user) do + Fabricate(:user, email: 'x@y.com', password: 'abcdefgh', + otp_required_for_login: true, otp_secret: User.generate_otp_secret(32)) + end + let(:recovery_codes) do + codes = user.generate_otp_backup_codes! + user.save + return codes + end + + context 'using a valid OTP' do + before do + post :create, params: { user: { otp_attempt: user.current_otp } }, session: { otp_user_id: user.id } + end + + it 'redirects to home' do + expect(response).to redirect_to(root_path) + end + + it 'logs the user in' do + expect(controller.current_user).to eq user + end + end + + context 'using a valid recovery code' do + before do + post :create, params: { user: { otp_attempt: recovery_codes.first } }, session: { otp_user_id: user.id } + end + + it 'redirects to home' do + expect(response).to redirect_to(root_path) + end + + it 'logs the user in' do + expect(controller.current_user).to eq user + end + end + + context 'using an invalid OTP' do + before do + post :create, params: { user: { otp_attempt: 'wrongotp' } }, session: { otp_user_id: user.id } + end + + it 'shows a login error' do + expect(flash[:alert]).to match I18n.t('users.invalid_otp_token') + end + + it "doesn't log the user in" do + expect(controller.current_user).to be_nil + end + end end end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index eb2a4aaea..4ed1d5a0a 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -1,6 +1,9 @@ require 'rails_helper' +require 'devise_two_factor/spec_helpers' RSpec.describe User, type: :model do + it_behaves_like 'two_factor_backupable' + describe 'validations' do it 'is invalid without an account' do user = Fabricate.build(:user, account: nil) |