Skip to content

Commit

Permalink
finish!
Browse files Browse the repository at this point in the history
  • Loading branch information
hhorikawa committed May 15, 2022
1 parent 85afc3c commit 9ae8ff8
Show file tree
Hide file tree
Showing 23 changed files with 171 additions and 122 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,8 @@ vendor/bundle/*

# Ignore master key for decrypting credentials and more.
/config/master.key

Gemfile.lock
yarn.lock

*~
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@ gem 'jbuilder', '~> 2.5'
gem 'bootsnap', '>= 1.1.0', require: false

gem 'google-authenticator-rails', '~> 2.0'
gem 'sorcery'
# CVE-2016-11086 `oauth` dependency
gem 'sorcery', '>= 0.16.1'

group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
Expand Down
11 changes: 8 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
# README

sorceryで二段階認証を実装したサンプルコード
# Sorceryで二段階認証 2FA を実装したサンプルコード

ちょっとした解説を書いたブログ
https://yu1056y.hatenablog.com/entry/2019/04/21/164540

その元ネタ. Rails v4.2, google-authenticator-rails v1.2.
https://moneyforward.com/engineers_blog/2015/08/06/google-authenticator-rails/

マネーフォワードエンジニアブログのほうはよいが、それを元にしたゆいな (id:yu1056y) 氏のソースコードは、控えめに言って問題が多く、作り直した。


14 changes: 11 additions & 3 deletions app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,28 @@
class ApplicationController < ActionController::Base
# ポカ避けのため, 基底クラスで require_login し, 不要な時は派生クラスで skip
# する.
# Sorcery::Controller::InstanceMethods で定義される.
before_action :require_login

before_action :check_mfa


private

# 未ログインの状態で要ログインのページにアクセスした時に呼び出される
def not_authenticated
redirect_to login_path, alert: 'Please login first'
end

# For before_action
# ログイン後、mfa を強制
def check_mfa
if !(user_mfa_session = UserMfaSession.find) && (user_mfa_session ? user_mfa_session.record == current_user : !user_mfa_session)
logout
redirect_to new_session_path
return if !current_user

if !(user_mfa_session = UserMfaSession.find) ||
user_mfa_session.record.email != current_user.email
#logout
redirect_to new_user_mfa_session_path
end
end
end
36 changes: 17 additions & 19 deletions app/controllers/session_controller.rb
Original file line number Diff line number Diff line change
@@ -1,32 +1,30 @@
# -*- coding:utf-8 -*-
# frozen_string_literal: true

# 単数形
class SessionController < ApplicationController
skip_before_action :require_login, expect: [:destroy]
prepend_before_action :authenticate_with_two_factor, only: :create
skip_before_action :check_mfa
skip_before_action :require_login, only: [:new, :create]
# ログイン後, MFAせずにログアウトの経路がある
skip_before_action :check_mfa, only: [:destroy]

# GET /login
def new
@user = User.new
end

def create; end
# POST /session
def create
if login(params[:email], params[:password])
redirect_back_or_to('/', notice: 'Login successful')
else
flash[:alert] = 'Login failed'
render 'new'
end
end

# POST /logout
def destroy
logout
redirect_to(:users, notice: 'Logged out!')
redirect_to '/', notice: 'Logged out!'
end

private

def authenticate_with_two_factor
user = User.find_by(email: params[:email])
if user.blank?
render action: 'new'
return
end
session[:email] = params[:email]
session[:password] = params[:password]

redirect_to new_user_mfa_session_path
end
end
33 changes: 33 additions & 0 deletions app/controllers/user_mfa_session_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
# -*- coding:utf-8 -*-
# frozen_string_literal: true

# 単数形
class UserMfaSessionController < ApplicationController
# 2FAなので、ユーザログインは必須.
#skip_before_action :require_login
skip_before_action :check_mfa

# GET /user_mfa_session/new
def new
@user = current_user
raise "internal error" if !@user.google_secret
end

# POST /user_mfa_session
def create
@user = current_user
if @user.google_authentic?(params[:auth][:mfa_code])
# persistence_token はクッキーのキー.
unless @user.persistence_token
@user.persistence_token = SecureRandom.hex
@user.save!
end
UserMfaSession.create(@user)
redirect_to user_path(@user), notice: 'MFA successful'
else
flash[:alert] = "Wrong code"
render :new
end
end

end
37 changes: 0 additions & 37 deletions app/controllers/user_mfa_sessions_controller.rb

This file was deleted.

8 changes: 5 additions & 3 deletions app/controllers/users_controller.rb
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
# -*- coding:utf-8 -*-
# frozen_string_literal: true

class UsersController < ApplicationController
before_action :set_user, only: %i[show edit update destroy]
skip_before_action :require_login, only: %i[index new create]
skip_before_action :check_mfa
# 手抜いて users コントローラでユーザ登録しているが、後段の本人確認などを行う
# ため, account コントローラなどを作った方がよい.
skip_before_action :require_login, only: %i[new create]

# GET /users
# GET /users.json
Expand All @@ -28,7 +30,7 @@ def edit; end
def create
@user = User.new(user_params)
if @user.save
redirect_to(:users, notice: 'User was successfully created')
redirect_to '/', notice: 'User was successfully created'
else
render :new
end
Expand Down
1 change: 1 addition & 0 deletions app/controllers/welcome_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
class WelcomeController < ApplicationController
skip_before_action :require_login

# GET /
def index
end
end
13 changes: 8 additions & 5 deletions app/models/user.rb
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,15 @@ class User < ApplicationRecord
validates :email, uniqueness: true
validates :email, presence:true

acts_as_google_authenticated lookup_token: :persistence_token, drift: 30, issuer: 'test_twofactor_auth'

before_create { |record|
record.persistence_token = SecureRandom.hex unless record.persistence_token
}
# lookup_token: カラム名. デフォルトは `persistence_token`. クッキーに保存す
# るときのキー.
# drift: 許容する秒数
# issuer: Google 認証システムの画面に表示される.
acts_as_google_authenticated drift: 30, issuer: 'test_twofactor_auth'
after_create { |record|
# GoogleAuthenticatorRails::generate_secret の値が設定される. ユーザごと.
record.set_google_secret
# persistence_token はここで設定する必要ない
#record.persistence_token = SecureRandom.hex unless record.persistence_token
}
end
2 changes: 2 additions & 0 deletions app/models/user_mfa_session.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# -*- coding:utf-8 -*-
# frozen_string_literal: true

# データベースに保存されない. -> クッキーに保存される
class UserMfaSession < GoogleAuthenticatorRails::Session::Base
end
60 changes: 39 additions & 21 deletions app/views/layouts/application.html.erb
Original file line number Diff line number Diff line change
@@ -1,27 +1,45 @@
<!DOCTYPE html>
<html>
<head>
<title>Tutorial</title>
<%= stylesheet_link_tag "application" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body>
<head>
<title>Tutorial</title>
<%= stylesheet_link_tag "application" %>
<%= javascript_include_tag "application" %>
<%= csrf_meta_tags %>
</head>
<body>

<div id="nav">
<% if current_user %>
<%= link_to "Edit Profile", edit_user_path(current_user.id) %>
<%= link_to "Logout", :logout, method: :post %>
<% else %>
<nav class="navbar navbar-expand-lg bg-light">
<div class="container-fluid">
<a class="navbar-brand" >2FA Sample</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse"
data-bs-target="#navbarTogglerDemo02"
aria-controls="navbarTogglerDemo02" aria-expanded="false"
aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarTogglerDemo02">
<% if current_user %>
Logged-in: <%= current_user.email %>
<%= link_to "Edit Profile...", edit_user_path(current_user.id) %>
<%= link_to "Logout", logout_path, method: :post %>
<% else %>
<%= link_to "Register", new_user_path %> |
<%= link_to "Login", :login %>
<% end %>
</div>
<div>
<p id="notice"><%= flash[:notice] %></p>
<p id="alert"><%= flash[:alert] %></p>
</div>
<%= yield %>
<%= link_to "Log In", login_path %>
<% end %>
</div>
</div>
</nav>

</body>
<div>
<% if (notice = flash[:notice]) %>
<p id="notice"><%= notice %></p>
<% end %>
<% if (alert = flash[:alert]) %>
<p id="alert"><%= alert %></p>
<% end %>
</div>

<%= yield %>

</body>
</html>
5 changes: 3 additions & 2 deletions app/views/session/_form.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<%= form_tag sessions_path, :method => :post do %>

<%= form_tag session_path, :method => :post do %>
<div class="field">
<%= label_tag :email %><br />
<%= text_field_tag :email %>
Expand All @@ -10,4 +11,4 @@
<div class="actions">
<%= submit_tag "Login" %>
</div>
<% end %>
<% end %>
9 changes: 7 additions & 2 deletions app/views/session/new.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
<h1>Login</h1>

<h1>Log In</h1>

<% if current_user %>
別のユーザでログインする
<% end %>

<%= render 'form' %>

<%= link_to 'Back', sessions_path %>

14 changes: 14 additions & 0 deletions app/views/user_mfa_session/new.html.erb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@

<% if !@user.persistence_token %>
<p>Google認証システムを開き、[+追加] から, QRコードをスキャンしてください。<br />
<%= image_tag @user.google_qr_uri %>
<% end %>

<p>Google認証システムに表示される 6桁のコードを入力してください。

<%= form_tag user_mfa_session_path, method: :post do %>
<div class="actions">
<%= text_field :auth, :mfa_code %>
<%= submit_tag 'authenticate' %>
</div>
<% end %>
12 changes: 0 additions & 12 deletions app/views/user_mfa_sessions/new.html.erb

This file was deleted.

2 changes: 0 additions & 2 deletions app/views/users/index.html.erb
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
<p id="notice"><%= notice %></p>

<h1>Users</h1>

<table>
Expand Down
1 change: 0 additions & 1 deletion app/views/users/new.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@

<%= render 'form', user: @user %>

<%= link_to 'Back', users_path %>
3 changes: 2 additions & 1 deletion app/views/users/show.html.erb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
<p id="notice"><%= notice %></p>

<h1>User <%= @user.email %></h1>

<p>
<strong>Email:</strong>
Expand Down
File renamed without changes.
Loading

0 comments on commit 9ae8ff8

Please sign in to comment.