* Fix #795, fix #704, fix #835 - 2FA requires confirmation to be enabled TOTP secret is not shown again after 2FA is enabled * Clean up
This commit is contained in:
		
					parent
					
						
							
								470eb0042e
							
						
					
				
			
			
				commit
				
					
						9acdb166e8
					
				
			
		
					 8 changed files with 88 additions and 28 deletions
				
			
		|  | @ -25,6 +25,10 @@ code { | ||||||
|     margin-bottom: 15px; |     margin-bottom: 15px; | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  |   strong { | ||||||
|  |     font-weight: 500; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|   .label_input { |   .label_input { | ||||||
|     display: flex; |     display: flex; | ||||||
| 
 | 
 | ||||||
|  | @ -224,7 +228,12 @@ code { | ||||||
|   } |   } | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | .qr-wrapper { | ||||||
|  |   display: flex; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| .qr-code { | .qr-code { | ||||||
|  |   flex: 0 0 auto; | ||||||
|   background: #fff; |   background: #fff; | ||||||
|   padding: 4px; |   padding: 4px; | ||||||
|   margin-bottom: 20px; |   margin-bottom: 20px; | ||||||
|  | @ -236,3 +245,13 @@ code { | ||||||
|     margin: 0; |     margin: 0; | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | .qr-alternative { | ||||||
|  |   margin-left: 10px; | ||||||
|  |   color: $color3; | ||||||
|  | 
 | ||||||
|  |   samp { | ||||||
|  |     display: block; | ||||||
|  |     font-size: 14px; | ||||||
|  |   } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | @ -5,19 +5,29 @@ class Settings::TwoFactorAuthsController < ApplicationController | ||||||
| 
 | 
 | ||||||
|   before_action :authenticate_user! |   before_action :authenticate_user! | ||||||
| 
 | 
 | ||||||
|   def show |   def show; end | ||||||
|     return unless current_user.otp_required_for_login |  | ||||||
| 
 | 
 | ||||||
|     @provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Rails.configuration.x.local_domain) |   def new | ||||||
|     @qrcode        = RQRCode::QRCode.new(@provision_url) |     redirect_to settings_two_factor_auth_path if current_user.otp_required_for_login | ||||||
|  | 
 | ||||||
|  |     @confirmation = Form::TwoFactorConfirmation.new | ||||||
|  |     current_user.otp_secret = User.generate_otp_secret(32) | ||||||
|  |     current_user.save! | ||||||
|  |     set_qr_code | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def enable |   def create | ||||||
|     current_user.otp_required_for_login = true |     if current_user.validate_and_consume_otp!(confirmation_params[:code]) | ||||||
|     current_user.otp_secret = User.generate_otp_secret |       current_user.otp_required_for_login = true | ||||||
|     current_user.save! |       current_user.save! | ||||||
| 
 | 
 | ||||||
|     redirect_to settings_two_factor_auth_path |       redirect_to settings_two_factor_auth_path, notice: I18n.t('two_factor_auth.enabled_success') | ||||||
|  |     else | ||||||
|  |       @confirmation = Form::TwoFactorConfirmation.new | ||||||
|  |       set_qr_code | ||||||
|  |       flash.now[:alert] = I18n.t('two_factor_auth.wrong_code') | ||||||
|  |       render action: :new | ||||||
|  |     end | ||||||
|   end |   end | ||||||
| 
 | 
 | ||||||
|   def disable |   def disable | ||||||
|  | @ -26,4 +36,15 @@ class Settings::TwoFactorAuthsController < ApplicationController | ||||||
| 
 | 
 | ||||||
|     redirect_to settings_two_factor_auth_path |     redirect_to settings_two_factor_auth_path | ||||||
|   end |   end | ||||||
|  | 
 | ||||||
|  |   private | ||||||
|  | 
 | ||||||
|  |   def set_qr_code | ||||||
|  |     @provision_url = current_user.otp_provisioning_uri(current_user.email, issuer: Rails.configuration.x.local_domain) | ||||||
|  |     @qrcode        = RQRCode::QRCode.new(@provision_url) | ||||||
|  |   end | ||||||
|  | 
 | ||||||
|  |   def confirmation_params | ||||||
|  |     params.require(:form_two_factor_confirmation).permit(:code) | ||||||
|  |   end | ||||||
| end | end | ||||||
|  |  | ||||||
							
								
								
									
										7
									
								
								app/models/form/two_factor_confirmation.rb
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								app/models/form/two_factor_confirmation.rb
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,7 @@ | ||||||
|  | # frozen_string_literal: true | ||||||
|  | 
 | ||||||
|  | class Form::TwoFactorConfirmation | ||||||
|  |   include ActiveModel::Model | ||||||
|  | 
 | ||||||
|  |   attr_accessor :code | ||||||
|  | end | ||||||
							
								
								
									
										17
									
								
								app/views/settings/two_factor_auths/new.html.haml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								app/views/settings/two_factor_auths/new.html.haml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | - content_for :page_title do | ||||||
|  |   = t('settings.two_factor_auth') | ||||||
|  | 
 | ||||||
|  | = simple_form_for @confirmation, url: settings_two_factor_auth_path, method: :post do |f| | ||||||
|  |   %p.hint= t('two_factor_auth.instructions_html') | ||||||
|  | 
 | ||||||
|  |   .qr-wrapper | ||||||
|  |     .qr-code= raw @qrcode.as_svg(padding: 0, module_size: 4) | ||||||
|  | 
 | ||||||
|  |     .qr-alternative | ||||||
|  |       %p.hint= t('two_factor_auth.manual_instructions') | ||||||
|  |       %samp.qr-alternative__code= current_user.otp_secret.scan(/.{4}/).join(' ') | ||||||
|  | 
 | ||||||
|  |   = f.input :code, hint: t('two_factor_auth.code_hint'), placeholder: t('simple_form.labels.defaults.otp_attempt') | ||||||
|  | 
 | ||||||
|  |   .actions | ||||||
|  |     = f.button :button, t('two_factor_auth.enable'), type: :submit | ||||||
|  | @ -2,16 +2,9 @@ | ||||||
|   = t('settings.two_factor_auth') |   = t('settings.two_factor_auth') | ||||||
| 
 | 
 | ||||||
| .simple_form | .simple_form | ||||||
|  |   %p.hint= t('two_factor_auth.description_html') | ||||||
|  | 
 | ||||||
|   - if current_user.otp_required_for_login |   - if current_user.otp_required_for_login | ||||||
|     %p.hint= t('two_factor_auth.instructions_html') |  | ||||||
| 
 |  | ||||||
|     .qr-code= raw @qrcode.as_svg(padding: 0, module_size: 5) |  | ||||||
| 
 |  | ||||||
|     %p.hint= t('two_factor_auth.plaintext_secret_html', secret: current_user.otp_secret) |  | ||||||
| 
 |  | ||||||
|     %p.hint= t('two_factor_auth.warning') |  | ||||||
| 
 |  | ||||||
|     = link_to t('two_factor_auth.disable'), disable_settings_two_factor_auth_path, data: { method: 'POST' }, class: 'block-button' |     = link_to t('two_factor_auth.disable'), disable_settings_two_factor_auth_path, data: { method: 'POST' }, class: 'block-button' | ||||||
|   - else |   - else | ||||||
|     %p.hint= t('two_factor_auth.description_html') |     = link_to t('two_factor_auth.setup'), new_settings_two_factor_auth_path, class: 'block-button' | ||||||
|     = link_to t('two_factor_auth.enable'), enable_settings_two_factor_auth_path, data: { method: 'POST' }, class: 'block-button' |  | ||||||
|  |  | ||||||
|  | @ -94,6 +94,10 @@ en: | ||||||
|       following: Following list |       following: Following list | ||||||
|     upload: Upload |     upload: Upload | ||||||
|   landing_strip_html: <strong>%{name}</strong> is a user on <strong>%{domain}</strong>. You can follow them or interact with them if you have an account anywhere in the fediverse. If you don't, you can <a href="%{sign_up_path}">sign up here</a>. |   landing_strip_html: <strong>%{name}</strong> is a user on <strong>%{domain}</strong>. You can follow them or interact with them if you have an account anywhere in the fediverse. If you don't, you can <a href="%{sign_up_path}">sign up here</a>. | ||||||
|  |   media_attachments: | ||||||
|  |     validations: | ||||||
|  |       images_and_video: Cannot attach a video to a status that already contains images | ||||||
|  |       too_many: Cannot attach more than 4 files | ||||||
|   notification_mailer: |   notification_mailer: | ||||||
|     digest: |     digest: | ||||||
|       body: 'Here is a brief summary of what you missed on %{instance} since your last visit on %{since}:' |       body: 'Here is a brief summary of what you missed on %{instance} since your last visit on %{since}:' | ||||||
|  | @ -152,18 +156,18 @@ en: | ||||||
|     formats: |     formats: | ||||||
|       default: "%b %d, %Y, %H:%M" |       default: "%b %d, %Y, %H:%M" | ||||||
|   two_factor_auth: |   two_factor_auth: | ||||||
|  |     code_hint: Enter the code generated by your authenticator app to confirm | ||||||
|     description_html: If you enable <strong>two-factor authentication</strong>, logging in will require you to be in possession of your phone, which will generate tokens for you to enter. |     description_html: If you enable <strong>two-factor authentication</strong>, logging in will require you to be in possession of your phone, which will generate tokens for you to enter. | ||||||
|     disable: Disable |     disable: Disable | ||||||
|     enable: Enable |     enable: Enable | ||||||
|     instructions_html: "<strong>Scan this QR code into Google Authenticator or a similiar app on your phone</strong>. From now on, that app will generate tokens that you will have to enter when logging in." |     enabled_success: Two-factor authentication successfully enabled | ||||||
|     plaintext_secret_html: 'Plain-text secret: <samp>%{secret}</samp>' |     instructions_html: "<strong>Scan this QR code into Google Authenticator or a similiar TOTP app on your phone</strong>. From now on, that app will generate tokens that you will have to enter when logging in." | ||||||
|  |     manual_instructions: 'If you can''t scan the QR code and need to enter it manually, here is the plain-text secret:' | ||||||
|  |     setup: Set up | ||||||
|     warning: If you cannot configure an authenticator app right now, you should click "disable" or you won't be able to login. |     warning: If you cannot configure an authenticator app right now, you should click "disable" or you won't be able to login. | ||||||
|  |     wrong_code: The entered code was invalid! Are server time and device time correct? | ||||||
|   users: |   users: | ||||||
|     invalid_email: The e-mail address is invalid |     invalid_email: The e-mail address is invalid | ||||||
|     invalid_otp_token: Invalid two-factor code |     invalid_otp_token: Invalid two-factor code | ||||||
|   will_paginate: |   will_paginate: | ||||||
|     page_gap: "…" |     page_gap: "…" | ||||||
|   media_attachments: |  | ||||||
|     validations: |  | ||||||
|       too_many: Cannot attach more than 4 files |  | ||||||
|       images_and_video: Cannot attach a video to a status that already contains images |  | ||||||
|  |  | ||||||
|  | @ -8,7 +8,7 @@ SimpleNavigation::Configuration.run do |navigation| | ||||||
|       settings.item :profile, safe_join([fa_icon('user fw'), t('settings.edit_profile')]), settings_profile_url |       settings.item :profile, safe_join([fa_icon('user fw'), t('settings.edit_profile')]), settings_profile_url | ||||||
|       settings.item :preferences, safe_join([fa_icon('sliders fw'), t('settings.preferences')]), settings_preferences_url |       settings.item :preferences, safe_join([fa_icon('sliders fw'), t('settings.preferences')]), settings_preferences_url | ||||||
|       settings.item :password, safe_join([fa_icon('cog fw'), t('auth.change_password')]), edit_user_registration_url |       settings.item :password, safe_join([fa_icon('cog fw'), t('auth.change_password')]), edit_user_registration_url | ||||||
|       settings.item :two_factor_auth, safe_join([fa_icon('mobile fw'), t('settings.two_factor_auth')]), settings_two_factor_auth_url |       settings.item :two_factor_auth, safe_join([fa_icon('mobile fw'), t('settings.two_factor_auth')]), settings_two_factor_auth_url, highlights_on: %r{/settings/two_factor_auth} | ||||||
|       settings.item :import, safe_join([fa_icon('cloud-upload fw'), t('settings.import')]), settings_import_url |       settings.item :import, safe_join([fa_icon('cloud-upload fw'), t('settings.import')]), settings_import_url | ||||||
|       settings.item :export, safe_join([fa_icon('cloud-download fw'), t('settings.export')]), settings_export_url |       settings.item :export, safe_join([fa_icon('cloud-download fw'), t('settings.export')]), settings_export_url | ||||||
|       settings.item :authorized_apps, safe_join([fa_icon('list fw'), t('settings.authorized_apps')]), oauth_authorized_applications_url |       settings.item :authorized_apps, safe_join([fa_icon('list fw'), t('settings.authorized_apps')]), oauth_authorized_applications_url | ||||||
|  |  | ||||||
|  | @ -60,9 +60,8 @@ Rails.application.routes.draw do | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
| 
 | 
 | ||||||
|     resource :two_factor_auth, only: [:show] do |     resource :two_factor_auth, only: [:show, :new, :create] do | ||||||
|       member do |       member do | ||||||
|         post :enable |  | ||||||
|         post :disable |         post :disable | ||||||
|       end |       end | ||||||
|     end |     end | ||||||
|  |  | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue