最近、Laravelを触っていて、2要素認証の機能を入れられないか調べてみたら、Google Authenticatorを利用した方法があったので、試してみた。
ライブラリは、google2faを利用。
実際の案件で使っていないので、実用化するにはもっと色々考慮しないといけないと思うけど、お試しということで。
前準備
前準備として、今回はLaravelの5.7を使ってここの手順で、基本的な認証機能を構築。
.env
ファイルを環境に合わせて編集して、
php artisan make:auth
php artisan migrate
で認証機能を実装。
google2faの導入
google2faをcomposerでインストール
composer require pragmarx/google2fa
QRコードのライブラリをcomposerでインストール
QRコードを導入したいので、QRコードのライブラリBaconQrCodeもインストール
composer require bacon/bacon-qr-code
secret keyの登録
カラムの追加
マイグレーションファイルを作成して、usersテーブルにsecret key用のカラムを追加
php artisan make:migration add_column_g2fa_key_users_table --table=users
カラム名などはお好みで。down()
は省略。
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('g2fa_key')->nullable(true);
});
}
マイグレーション実行
php artisan migrate
Secret Keyの登録
今回は、ユーザー登録時にSecret Keyを登録するようにする。
class User extends Authenticatable
{
protected $fillable = [
'name', 'email', 'password', 'g2fa_key', // g2fa_keyを追加
];
}
use PragmaRX\Google2FA\Google2FA; // 追加
class RegisterController extends Controller
{
protected function create(array $data)
{
// SecretKeyを生成
$g2fa = new Google2FA();
$key = $g2fa->generateSecretKey();
return User::create([
'name' => $data['name'],
'email' => $data['email'],
'password' => Hash::make($data['password']),
'g2fa_key' => $key, // カラムに登録
]);
}
}
http://{your domain}/register
からユーザー登録して、g2fa_keyにキーが登録されていればOK。
QRコードの作成
homeにGoogle Authenticatorで読み取る用のQRコードを表示する。
use Illuminate\Support\Facades\Auth; // 追加
use PragmaRX\Google2FA\Google2FA; // 追加
class HomeController extends Controller
{
public function index()
{
$user = Auth::user();
$g2fa = new Google2FA();
// $g2fa->setAllowInsecureCallToGoogleApis(true);
$qrUrl = $g2fa->getQRCodeGoogleUrl(
config('app.name'),
$user->email,
$user->g2fa_key
);
return view('home', ['qr' => $qrUrl]);
}
}
おそらくだけど、ローカル環境はSSL対応ではないので、$g2fa->setAllowInsecureCallToGoogleApis(true);
を追加する必要がある。
@extends('layouts.app')
@section('content')
<div class="container">
<div class="row justify-content-center">
<div class="col-md-8">
<div class="card">
<div class="card-header">Dashboard</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
<img src="{{ $qr }}"> {{-- 追加 --}}
</div>
</div>
</div>
</div>
</div>
@endsection
QRコードが表示されるので、Google AuthenticatorアプリでQRを読み込む。
認証処理
ログイン画面側を作っていく
Viewの修正
ログインフォームに認証キーの追加。一部だけ抜粋。
<div class="form-group row">
<label for="secret_key" class="col-md-4 col-form-label text-md-right">{{ __('認証キー') }}</label>
<div class="col-md-6">
<input id="secret_key" type="text" class="form-control{{ $errors->has('secret_key') ? ' is-invalid' : '' }}" name="secret_key" value="{{ old('secret_key') }}" required>
@if ($errors->has('secret_key'))
<span class="invalid-feedback" role="alert">
<strong>{{ $errors->first('secret_key') }}</strong>
</span>
@endif
</div>
</div>
LoginControllerの修正
LoginControllerに2要素認証のログインロジックを追加。追加した部分だけ抜粋。
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use PragmaRX\Google2FA\Google2FA;
class LoginController extends Controller
{
public function authenticate(Request $request)
{
$email = $request->input('email');
$password = $request->input('password');
$secretKey = $request->input('secret_key');
$credentials = ['email' => $email, 'password' => $password];
if (Auth::once($credentials)) {
$user = Auth::user();
if ($user->g2fa_key) {
$g2fa = new Google2FA();
if (!$g2fa->verifyKey($user->g2fa_key, $secretKey)) {
return redirect()->route('login');
}
Auth::attempt($credentials);
}
return redirect()->intended($this->redirectTo);
}
return redirect()->route('login');
}
}
routerにログイン処理を追加
追加したメソッドでログイン処理を行うように、routerに下記を追加。
Route::post('login', 'Auth\LoginController@authenticate');
あとは、ログイン画面でログイン情報とGoogle Authenticatorで発行された番号を入力してログインできればOK
今回は、2段階ではなく2要素認証という形で試してみた。
バリデーションエラーの時とか、2段階にしたい時とかもっと考慮すべきことはたくさんあると思うけど、参考までにということで。
Laravelが触り始めたばかりでよくわかってないので、ログイン処理もお作法的に良いかなどは疑問。
あとは、Google2FA for Laravelというのもあるらしい。
今回試したソースは、ここにアップしてます。
参考サイト