Laravel8から認証基盤としてJetstreamが登場しました。それ以外にもLaravel BreezeというJetstreamをすっきりシンプルにしたものもあります。
つまり認証基盤をスキャフォールド的にパパっと構築したい場合はJetstreamかBreezeの2択になりますが、Jetstreamが2要素認証に対応しているのに対して、Breezeは2要素認証に対応していないようです。
つまり2要素認証を組み上げたい場合はJetstreamを使うしかないのですが、JetstreamはフロントエンドをLivewireかInertiaで構築する必要があります。
今回はフロントエンドをただのBladeで用意したいorフロントエンドを分けてAPIを叩きたいという目的のために、2要素認証をFortifyで構築していく方法をご紹介します。
JetstreamとBreezeがフロントエンドやルートなどをひっくるめてスキャフォールドとして認証基盤をコマンド1つで構築してくれるのに対して、Fortifyが提供するのはあくまで認証の仕組みだけです。実装は自分で行う必要があります。
また、Laravel UIという昔からあるbladeベースの認証基盤もありますが、BreezeではなくUIを今から使うメリットはあまり無いと思います。
ちなみに、UI/BreezeとFortifyを両方導入した場合、デフォルトだとFortifyの認証システムは機能しません(スキャフォールドに負ける)。よってFortifyのプロバイダなどで2要素認証の仕組みを組み込んでも、2要素認証を含めてFortify側の認証機能は働きません。
Fortifyを使う時はFortify単独で使用します。
LaravelFortifyをいつ使用するのが適切か疑問に思われるかもしれません。まず、Laravelのアプリケーションスターターキットで説明されているいずれかを使用している場合、Laravelのすべてのアプリケーションスターターキットはあらかじめ完全な認証実装を提供しているため、LaravelFortifyをインストールする必要はありません。 。
アプリケーションスターターキットを使用しておらず、アプリケーションに認証機能が必要な場合は、アプリケーションの認証機能を自分で実装するか、Laravel Fortifyを使用してこうした機能のバックエンド実装を提供するか、2つのオプションがあります。
「Laravel 8.x Laravel Fortify」より引用
Contents
Fortifyの導入
まずはFortifyを導入します。次の流れを辿ってください。
Fortifyのインストール
composer require laravel/fortify
php artisan vendor:publish --provider="Laravel\Fortify\FortifyServiceProvider"
php artisan migrate
bootstrapのインストール
composer require laravel/ui
php artisan ui bootstrap
npm install && npm run dev
app.phpの編集
'providers' => [
// ...
App\Providers\FortifyServiceProvider::class,
],
FortifyServiceProvider.phpの編集
public function boot()
{
Fortify::loginView(fn () => view('auth.login'));
Fortify::registerView(fn () => view('auth.register'));
// ...
}
web.phpにルート追加
Route::get('/home', function () {
return view('home');
})->middleware('auth');
app.blade.phpの作成
ここからコピペしてresources/views/layouts/app.blade.phpを作成。
login.blade.phpの作成
ここからコピペしてresources/views/auth/login.blade.phpを作成。
register.blade.phpの作成
ここからコピペしてresources/views/auth/register.blade.phpを作成。
home.blade.phpの作成
@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">{{ __('Two factor Authentication') }}</div>
<div class="card-body">
@if (session('status'))
<div class="alert alert-success" role="alert">
{{ session('status') }}
</div>
@endif
{{ __('You are logged in!') }}
</div>
</div>
</div>
</div>
</div>
@endsection
動作確認
ここまででログイン周りの実装は完了です。実際に登録やログインが可能かどうかを確認してみてください。
2要素認証の実装
ここからが2要素認証の実装になります。2要素認証の実装については以下の動画を参考にしています。
User.phpにトレイトを追加
use Laravel\Fortify\TwoFactorAuthenticatable;
class User extends Authenticatable
{
use HasFactory, Notifiable, TwoFactorAuthenticatable;
// ...
}
FortifyServiceProvider.phpの編集
public function boot()
{
Fortify::confirmPasswordView(function () {
return view('auth.confirm-password');
});
Fortify::twoFactorChallengeView(function () {
return view('auth.two-factor-challenge');
});
// ...
}
home.blade.phpを修正
@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">{{ __('Two factor Authentication') }}</div>
<div class="card-body">
@if (session('status') === "two-factor-authentication-disabled")
<div class="alert alert-success" role="alert">
Two factor Authentication has been disabled.
</div>
@endif
@if (session('status') === "two-factor-authentication-enabled")
<div class="alert alert-success" role="alert">
Two factor Authentication has been enabled.
</div>
@endif
<form method="POST" action ="/user/two-factor-authentication">
@csrf
@if (auth()->user()->two_factor_secret)
@method('DELETE')
<div class="pb-5">
{!! auth()->user()->twoFactorQrCodeSvg() !!}
</div>
<div>
<h3>Recovery Codes:</h3>
<ul>
@foreach (json_decode(decrypt(auth()->user()->two_factor_recovery_codes)) as $code)
<li>{{ $code }}</li>
@endforeach
</ul>
</div>
<button class="btn btn-danger">Disable</button>
@else
<button class="btn btn-primary">Enable</button>
@endif
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
confirm-password.blade.phpの作成
ここからコピペしてresources/views/auth/confirm-password.blade.phpを作成。
two-factor-challenge.blade.phpの作成
@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">{{ __('Two factor Challenge') }}</div>
<div class="card-body">
{{ __('Please enter your authentication code to login.') }}
<form method="POST" action="{{ route('two-factor.login') }}">
@csrf
<div class="form-group row">
<label for="code" class="col-md-4 col-form-label text-md-right">{{ __('Code') }}</label>
<div class="col-md-6">
<input id="code" type="code" class="form-control @error('code') is-invalid @enderror" name="code" required autocomplete="current-code">
@error('code')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Submit') }}
</button>
</div>
</div>
</form>
</div>
</div>
<div class="card">
<div class="card-header">{{ __('Two factor Recovery Code') }}</div>
<div class="card-body">
{{ __('Please enter your authentication code to login.') }}
<form method="POST" action="{{ route('two-factor.login') }}">
@csrf
<div class="form-group row">
<label for="recovery_code" class="col-md-4 col-form-label text-md-right">{{ __('Reocvery Code') }}</label>
<div class="col-md-6">
<input id="recovery_code" type="recovery_code" class="form-control @error('recovery_code') is-invalid @enderror" name="recovery_code" required autocomplete="current-recovery_code">
@error('code')
<span class="invalid-feedback" role="alert">
<strong>{{ $message }}</strong>
</span>
@enderror
</div>
</div>
<div class="form-group row mb-0">
<div class="col-md-8 offset-md-4">
<button type="submit" class="btn btn-primary">
{{ __('Submit') }}
</button>
</div>
</div>
</form>
</div>
</div>
</div>
</div>
</div>
@endsection
Google AuthenticatorでQRコードを登録
これでFortifyを使った2要素認証の実装は完了です。
QRコードをGoogle Authenticatorで読み取ってください。また、リカバリコードでもログインできることを確認してみてください。
QRコードの表示場所を変える
ホーム画面に2要素認証のQRコードが表示されるのは変ですので、プロフィール画面を作ってそこに表示するようにします。
app.blade.phpの修正
app.blade.phpのリスト要素を以下のように修正します。
<li class="nav-item dropdown">
<a id="navbarDropdown" class="nav-link dropdown-toggle" href="#" role="button" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false" v-pre>
{{ Auth::user()->name }}
</a>
<div class="dropdown-menu dropdown-menu-right" aria-labelledby="navbarDropdown">
<a class="dropdown-item" href="{{ route('logout') }}"
onclick="event.preventDefault();
document.getElementById('logout-form').submit();">
{{ __('Logout') }}
</a>
<form id="logout-form" action="{{ route('logout') }}" method="POST" class="d-none">
@csrf
</form>
<a class="dropdown-item" href="{{ route('profile') }}">
{{ __('Profile') }}
</a>
</div>
</li>
web.phpの修正
ルートを以下のように書き換えます。
Route::middleware('auth')->group(function () {
Route::get('/home', function () {
return view('home');
});
Route::get('/profile', function() {
return view('profile.index');
})->name('profile');
});
profile/index.blade.phpの作成
home.blade.phpの内容をresources/views/profile/index.blade.phpにコピーしてください。home.blade.phpは@extends(‘layouts.app’)以外を削除します。
これでQRコードがhomeからいなくなります。