Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a variant which sets up 2FA with devise #77

Draft
wants to merge 19 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,9 @@ jobs:
- name: devise
config_path: "ci/configs/devise.yml"
skips: --skip-javascript
- name: devise_mfa
config_path: "ci/configs/devise_mfa.yml"
skips: --skip-javascript
- name: basic_with_skips
config_path: "ci/configs/basic.yml"
skips: --skip-spring --skip-javascript
Expand Down
1 change: 1 addition & 0 deletions ackama_rails_template.config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ use_typescript: true
# Use these flags to enable features in the rails app created by this template.
apply_variant_react: false
apply_variant_devise: false
apply_variant_devise_mfa: false
apply_variant_sidekiq: false
apply_variant_bootstrap: false

Expand Down
1 change: 1 addition & 0 deletions ci/configs/all-typescript.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use_typescript: true
apply_variant_github_actions_ci: true
apply_variant_react: true
apply_variant_devise: true
apply_variant_devise_mfa: true
apply_variant_sidekiq: true
apply_variant_bootstrap: true
apply_variant_deploy_with_capistrano: true
Expand Down
1 change: 1 addition & 0 deletions ci/configs/all.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use_typescript: false
apply_variant_github_actions_ci: true
apply_variant_react: true
apply_variant_devise: true
apply_variant_devise_mfa: true
apply_variant_sidekiq: true
apply_variant_bootstrap: true
apply_variant_deploy_with_capistrano: true
Expand Down
1 change: 1 addition & 0 deletions ci/configs/basic-typescript.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use_typescript: true
apply_variant_github_actions_ci: false
apply_variant_react: false
apply_variant_devise: false
apply_variant_devise_mfa: true
apply_variant_sidekiq: false
apply_variant_bootstrap: false
apply_variant_deploy_with_capistrano: false
Expand Down
1 change: 1 addition & 0 deletions ci/configs/basic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use_typescript: false
apply_variant_github_actions_ci: false
apply_variant_react: false
apply_variant_devise: false
apply_variant_devise_mfa: false
apply_variant_sidekiq: false
apply_variant_bootstrap: false
apply_variant_deploy_with_capistrano: false
Expand Down
1 change: 1 addition & 0 deletions ci/configs/bootstrap-typescript.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use_typescript: true
apply_variant_github_actions_ci: false
apply_variant_react: false
apply_variant_devise: false
apply_variant_devise_mfa: true
apply_variant_sidekiq: false
apply_variant_bootstrap: true
apply_variant_deploy_with_capistrano: false
Expand Down
1 change: 1 addition & 0 deletions ci/configs/bootstrap.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use_typescript: false
apply_variant_github_actions_ci: false
apply_variant_react: false
apply_variant_devise: false
apply_variant_devise_mfa: true
apply_variant_sidekiq: false
apply_variant_bootstrap: true
apply_variant_deploy_with_capistrano: false
Expand Down
1 change: 1 addition & 0 deletions ci/configs/deploy_with_ackama_ec2_capistrano.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ git_repo_url: ""
use_typescript: false
apply_variant_react: false
apply_variant_devise: false
apply_variant_devise_mfa: true
apply_variant_github_actions_ci: false
apply_variant_sidekiq: false
apply_variant_typescript: false
Expand Down
1 change: 1 addition & 0 deletions ci/configs/deploy_with_capistrano.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use_typescript: false
apply_variant_github_actions_ci: false
apply_variant_react: false
apply_variant_devise: false
apply_variant_devise_mfa: true
apply_variant_sidekiq: false
apply_variant_bootstrap: false
apply_variant_deploy_with_capistrano: true
Expand Down
1 change: 1 addition & 0 deletions ci/configs/devise.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ apply_variant_github_actions_ci: false
use_typescript: false
apply_variant_react: false
apply_variant_devise: true
apply_variant_devise_mfa: true
apply_variant_sidekiq: false
apply_variant_bootstrap: false
apply_variant_deploy_with_capistrano: false
Expand Down
13 changes: 13 additions & 0 deletions ci/configs/devise_mfa.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
---
staging_hostname: "staging.example.com"
production_hostname: "www.example.com"
git_repo_url: ""
apply_variant_github_actions_ci: false
use_typescript: false
apply_variant_react: false
apply_variant_devise: true
apply_variant_devise_mfa: true
apply_variant_sidekiq: false
apply_variant_bootstrap: false
apply_variant_deploy_with_capistrano: false
apply_variant_deploy_with_ackama_ec2_capistrano: false
1 change: 1 addition & 0 deletions ci/configs/github_actions.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use_typescript: false
apply_variant_github_actions_ci: true
apply_variant_react: false
apply_variant_devise: false
apply_variant_devise_mfa: true
apply_variant_sidekiq: false
apply_variant_bootstrap: false
apply_variant_deploy_with_capistrano: false
Expand Down
1 change: 1 addition & 0 deletions ci/configs/react-typescript.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use_typescript: true
apply_variant_github_actions_ci: false
apply_variant_react: true
apply_variant_devise: false
apply_variant_devise_mfa: true
apply_variant_sidekiq: false
apply_variant_bootstrap: false
apply_variant_deploy_with_capistrano: false
Expand Down
1 change: 1 addition & 0 deletions ci/configs/react.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use_typescript: false
apply_variant_github_actions_ci: false
apply_variant_react: true
apply_variant_devise: false
apply_variant_devise_mfa: false
apply_variant_sidekiq: false
apply_variant_bootstrap: false
apply_variant_deploy_with_capistrano: false
Expand Down
1 change: 1 addition & 0 deletions ci/configs/sidekiq.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ use_typescript: false
apply_variant_github_actions_ci: false
apply_variant_react: false
apply_variant_devise: false
apply_variant_devise_mfa: false
apply_variant_sidekiq: true
apply_variant_bootstrap: false
apply_variant_deploy_with_capistrano: false
Expand Down
9 changes: 8 additions & 1 deletion template.rb
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ def apply_variant_devise?
@yaml_config.fetch("apply_variant_devise")
end

def apply_variant_devise_mfa?
@yaml_config.fetch("apply_variant_devise")
end

def apply_variant_sidekiq?
@yaml_config.fetch("apply_variant_sidekiq")
end
Expand Down Expand Up @@ -180,7 +184,10 @@ def apply_template! # rubocop:disable Metrics/MethodLength, Metrics/AbcSize, Met

# we deliberately place this after the initial git commit because it
# contains a lot of changes and adds its own git commit
apply "variants/devise/template.rb" if TEMPLATE_CONFIG.apply_variant_devise?
if TEMPLATE_CONFIG.apply_variant_devise?
apply "variants/devise/template.rb"
apply "variants/devise-mfa/template.rb" if TEMPLATE_CONFIG.apply_variant_devise_mfa?
end

# We apply code annotation **after** all the other variants which might
# generate routes and models
Expand Down
11 changes: 11 additions & 0 deletions variants/devise-mfa/app/controllers/dashboards_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
##
# This is an example controller which you should remove from your application.
# It demonstrates how to create a controller which requires users to be
# authenticated before running any of its actions.
#
class DashboardsController < ApplicationController
# Only authenticated users can see the dashboard
def show
authorize :dashboard
end
end
14 changes: 14 additions & 0 deletions variants/devise-mfa/app/controllers/users/devise_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Users
class DeviseController < ApplicationController
# class DeviseController < Devise::DeviseController
# TODO: this doesn't really need to inherit from Application controller anymore
# Is it more or less surprising to
before_action :configure_permitted_parameters, if: :devise_controller?

protected

def configure_permitted_parameters
devise_parameter_sanitizer.permit(:sign_in, keys: [:otp_attempt])
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
##
# This controller allows users to manage their MFA credential(s). This
# controller is not involved in signing in or out.
#
# Conceptually each user has one MFA resource which they can
# manage because devise-two-factor only supports one mfa code per user
#
module Users
class MultiFactorAuthenticationsController < ApplicationController
before_action { authorize :mfa }

# Add the environment name to the two-factor authentication string so that
# you can more easily tell the MFA codes for different environments
# apart in your MFA app.
#
# Keep this name short. Some TOTP management applications (e.g. Google
# Authenticator) do not let users edit this name and do not show many
# characters on screen.
#
ISSUER = if Rails.env.production?
I18n.t("application.name").freeze
else
"#{I18n.t("application.name")} #{Rails.env}".freeze
end

# Allow the template to access issuer
helper_method :issuer

##
# #show is the entry point for a user managing their two-factor
# authentication. It displays a summary of the current state of their
# two-factor authentication setup and buttons/links to take actions on
# it.
#
def show
end

##
# #new starts the process of setting up two-factor authentication for
# the user
#
def new
@otp_secret = User.generate_otp_secret
end

##
# #create accepts the form submission from #new and checks that the TOTP
# code supplied by the user is valid. If the user gives us a valid TOTP code
# then we can we be confident they have correctly setup OTP so we can
# require it at next sign in.
def create
if current_user.validate_and_consume_otp!(otp_param, otp_secret: otp_secret_param)
current_user.update!(otp_secret: otp_secret_param, otp_required_for_login: true)
redirect_to users_multi_factor_authentication_path, notice: t(".success")
else
@otp_secret = otp_secret_param
flash.now[:alert] = t(".invalid_code")
render :new
end
end

def create_backup_codes
@backup_codes = current_user.generate_otp_backup_codes!
current_user.save!
end

def destroy_backup_codes
current_user.update!(otp_backup_codes: nil)
redirect_to users_multi_factor_authentication_path, notice: t(".success")
end

private

def otp_param
params.require(:otp_attempt).gsub(/\A[^\d+]\z/, "")
end

def otp_secret_param
params.require(:otp_secret)
end

def issuer
ISSUER
end
end
end
Loading