Created
March 19, 2019 16:26
-
-
Save aganov/1e5bb3b67540c4337b3a3590c1b11a38 to your computer and use it in GitHub Desktop.
devise two-factor two steps
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
# Controller concern to handle two-factor authentication | |
# Upon inclusion, skips `require_no_authentication` on `:create`. | |
module AuthenticatesWithTwoFactor | |
extend ActiveSupport::Concern | |
included do | |
# This action comes from DeviseController, but because we call `sign_in` | |
# manually, not skipping this action would cause a "You are already signed | |
# in." error message to be shown upon successful login. | |
skip_before_action :require_no_authentication, only: [:create], raise: false | |
before_action :reset_otp_user_id, only: [:new] | |
prepend_before_action :authenticate_with_two_factor, | |
if: :otp_required_for_login?, only: [:create] | |
end | |
private | |
def find_user | |
if session[:otp_user_id] | |
User.find(session[:otp_user_id]) | |
elsif sign_in_params[:email] | |
User.find_by(email: sign_in_params[:email]) | |
end | |
end | |
def reset_otp_user_id | |
session.delete(:otp_user_id) | |
end | |
def otp_required_for_login? | |
find_user.try :otp_required_for_login? | |
end | |
def valid_otp_attempt?(user) | |
user.validate_and_consume_otp!(sign_in_params[:otp_attempt]) || | |
user.invalidate_otp_backup_code!(sign_in_params[:otp_attempt]) | |
end | |
# Store the user's ID in the session for later retrieval and render the | |
# two factor code prompt | |
# | |
# The user must have been authenticated with a valid login and password | |
# before calling this method! | |
def prompt_for_two_factor(user) | |
self.resource = user | |
session[:otp_user_id] = user.id | |
render "devise/sessions/two_factor" | |
end | |
def authenticate_with_two_factor | |
self.resource = find_user | |
devise_parameter_sanitizer.permit(:sign_in, keys: [:otp_attempt]) | |
if sign_in_params[:otp_attempt].present? && session[:otp_user_id] | |
authenticate_with_two_factor_via_otp(resource) | |
elsif resource && resource.valid_password?(resource_params[:password]) | |
prompt_for_two_factor(resource) | |
end | |
end | |
def authenticate_with_two_factor_via_otp(user) | |
if valid_otp_attempt?(user) | |
reset_otp_user_id # Remove any lingering user data from login | |
sign_in(resource_name, resource) | |
else | |
flash.now[:alert] = t("devise.failure.invalid_opt_attempt") | |
prompt_for_two_factor(user) | |
end | |
end | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
Rails.application.routes.draw do | |
devise_for :users, controllers: { sessions: :sessions } | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# frozen_string_literal: true | |
class SessionsController < Devise::SessionsController | |
include AuthenticatesWithTwoFactor | |
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
- if resource.otp_required_for_login? | |
= simple_form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f| | |
.form-inputs | |
= f.input :otp_attempt, required: false, label: false, autofocus: true | |
.form-actions | |
= f.button :submit | |
= render "devise/shared/links" |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment