Laravelに2要素認証を実装しよう
LaravelにBreezeをインストールするとログイン認証が行えますが、ログイン時のアカウントのセキュリティを強化するには、スマホでの2要素認証の方法がおススメです。
第三者が不正にアカウントにアクセスするのを防止し、アカウントを保護することができます。
実装するとログインする際に、通常のパスワードのログインの後に、本人のスマホで生成されたワンタイムパスワードによる追加の認証情報が必要になります。
今回は、Google Authenticatorアプリを使って、処理を実装したいと思います。
作成環境は、Windows11、xampp、php8.1、VSCode、Laravel10です。
Google Authenticator
iOSやAndroidのスマホ用のアプリケーションで、2段階認証のセキュリティコードを生成するために使用されます。
アプリをダウンロードしてアクセスすると、認証コードを生成します。
コードの有効期限は30秒で、時間を過ぎると新たなコードを生成します。
Google Authenticator
Laravel+Breezeのインストール
まずは、プロジェクトの作成。Larabelインストールの方法です。Versionが変わっても一緒です。
適当なプロジェクト名をつけて、インストールしたいディレクトリにインストールしてください。Windowsとxamppを使用してるので、powerShellでhtdocsまできて、このコマンドを入れます。
composer create-project --prefer-dist laravel/laravel project-name
//laravelのバージョンを指定するなら,例:バージョン8
composer create-project --prefer-dist laravel/laravel project-name "8.*"
インストールが終わったら、config/app.phpの設定を変更します。
'timezone' => 'Asia/Tokyo',
'locale' => 'ja',
'faker_locale' => 'ja_JP',
DataBaseの設定をします。
このプロジェクトで使用するデータベースを作成して、自身のデータベース情報をenvに入力してください。
DB_CONNECTION=mysql
DB_HOST=127.0.0.1
DB_PORT=3306
DB_DATABASE=DataBase Name
DB_USERNAME=DataBase User
DB_PASSWORD=DataBase Password
次にUser認証のためのBreezeを入れます。
composerで読み込む。
composer require laravel/breeze --dev
終ったらインストール。私はblade派なのでblade.
React や Vue にも対応しています。
blade の部分をreact か vue に変えてください。
php artisan breeze:install blade
インストールが終わったら、データベースのマイグレーションをします。
もしusersテーブルに対して既存のカラムに変更を入れたいときは、先に変更してください。
database > migrations のなかにテーブル情報があります。
*後からでもできますが、どういうテーブルができるのか先に確認してください。
php artisan migrate
データベーステーブルもできたのでnpm install & run devします。
npm intall
npm run dev
これでサーバーを立ち上げたらLaravel のインストールは終わりです。
php artisan serve
google2fa インストール
vscodeのターミナルで次のコマンドを入力します。
laravel用のgoogle2faです。
composer require pragmarx/google2fa-laravel
少し時間がかかります。終ったら次を入力します。
qrコードを生成してくれます。
composer require bacon/bacon-qr-code
設定ファイルをpublishしてconfigに作成します。
php artisan vendor:publish --provider="PragmaRX\Google2FALaravel\ServiceProvider"
これでconfigフォルダの中にgoogle2fa.phpができています。
google2faに関する細かい設定・情報が記載されています。
kernelのmiddlewareに2fa
で呼び出せるように追加します。
app > Http > Kernel.php
/**
* The application's route middleware.
*
* These middleware may be assigned to groups or used individually.
*
* @var array
*/
protected $routeMiddleware = [
...
'2fa' => \PragmaRX\Google2FALaravel\Middleware::class,
];
Migrationする
認証コードの判定用に使用する専用のカラムがデータベースに必要なので、Userテーブルにカラムを追加します。
php artisan make:migration add_goole_2fa_columns_to_users_table --table=users
できたmigrationファイルを編集します。
google2fa_secretは認証コード
return new class extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::table('users', function (Blueprint $table) {
$table->string('google2fa_secret')
->nullable()
->after('remember_token');
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::table('users', function (Blueprint $table) {
$table->dropColumn('google2fa_secret');
});
}
};
修正したらターミナルで実行します。
php artisan migrate
Userモデルの修正
usersテーブルにカラムを追加したので、Userモデルを修正します。
app > Models > User.php
<?php
namespace App\Models;
// use Illuminate\Contracts\Auth\MustVerifyEmail;
use Illuminate\Database\Eloquent\Factories\HasFactory;
use Illuminate\Foundation\Auth\User as Authenticatable;
use Illuminate\Notifications\Notifiable;
use Laravel\Sanctum\HasApiTokens;
use Illuminate\Database\Eloquent\Casts\Attribute;//追加
class User extends Authenticatable
{
use HasApiTokens, HasFactory, Notifiable;
/**
*
* The attributes that are mass assignable.
*
* @var array<int, string>
*/
protected $fillable = [
'name',
'email',
'password',
'google2fa_secret',//追加
];
/**
* The attributes that should be hidden for serialization.
*
* @var array<int, string>
*/
protected $hidden = [
'password',
'remember_token',
'google2fa_secret'//追加
];
/**
* The attributes that should be cast.
*
* @var array<string, string>
*/
protected $casts = [
'email_verified_at' => 'datetime',
];
/**
* ここから追加
* Interact with the user's first name.
*
* @param string $value
* @return \Illuminate\Database\Eloquent\Casts\Attribute
*/
protected function google2faSecret(): Attribute
{
return new Attribute(
get: fn ($value) => decrypt($value),
set: fn ($value) => encrypt($value),
);
}
}
Routeを設定
ログインの処理が変わるので、Google2fa用の routes を設定します。
dashoboardを開くには、middlewareの2faの処理を通らないといけなくなります。
app > routes > auth.php
use App\Http\Controllers\ProfileController;//追加
use App\Http\Controllers\HomeController;//追加
Route::middleware('auth')->group(function () {
Route::get('/profile', [ProfileController::class, 'edit'])->name('profile.edit');
Route::patch('/profile', [ProfileController::class, 'update'])->name('profile.update');
Route::delete('/profile', [ProfileController::class, 'destroy'])->name('profile.destroy');
Route::middleware(['2fa', 'verified'])->group(function () {
Route::get('/dashboard', [HomeController::class, 'index'])->name('dashboard');
Route::post('/2fa', function () {
return redirect(route('dashboard'));
})->name('2fa');
});
});
User登録時の処理
user の新規登録の際は、本人識別用のコードをあらかじめ登録しておく必要があります。
RegisterController の登録処理にgoogle2faの識別コードを追加します。
app > Http > Controllers > Auth > RegisterController.php
$google2fa = app('pragmarx.google2fa');
$user = User::create([
'name' => $request->name,
'email' => $request->email,
'password' => Hash::make($request->password),
'google2fa_secret' => $google2fa->generateSecretKey(),
]);
ログイン時に認証コードを入力する画面を作る
viewsの中に、メールアドレスとパスワードで認証した後、アプリでのワンタイムパスワードを入力する画面を作成します。
Viewsの中に専用のbladeファイルを作成します。
app > resources > views > google2fa > index.blade.php
下記では、あらかじめ、publicフォルダの中にimagesをつくり、アプリのダウンロード用のアイコンを入れてあります。
<x-guest-layout>
<!-- Session Status -->
<x-auth-session-status class="mb-4" :status="session('status')" />
<!-- Validation Errors -->
@if($errors->any())
<div class="w-full mb-4">
<div class="alert alert-danger">
<strong>{{$errors->first()}}</strong>
</div>
</div>
@endif
@php
$qrCodeUrl = Google2FA::getQRCodeInline(
config('app.name'),
Auth::user()->email,
Auth::user()->google2fa_secret,
);
@endphp
<div>{!! $qrCodeUrl !!}</div>
<p class="mt-8">2要素認証には、スマホのGoogle Authenticatorアプリが必要です。以下からあなたのデバイスに合わせてインストールしてください。</p>
<div class="flex items-center gap-4 pb-4">
<div class="w-40">
<a
href="https://play.google.com/store/apps/details?id=com.google.android.apps.authenticator2&hl=ja&gl=US"><img
src="{{ asset('images/icons/google-play-badge.png') }}"
alt=""></a>
</div>
<div class="">
<a href="https://apps.apple.com/jp/app/google-authenticator/id388497605"><img
src="{{ asset('images/icons/Download_on_the_App_Store_Badge_JP_RGB_blk_100317.svg') }}"
alt="" class="w-36"></a>
</div>
</div>
<form method="POST" action="{{ route('2fa') }}">
@csrf
<div class="my-6">
<p class="text-sm mb-2">アプリに表示されている文字列を入力してください。30秒ごとに変わります。</p>
<label for="one_time_password" value="__('ワンタイムパスワード')" />
<input type="password" id="one_time_password" name="one_time_password"
class="block w-full rounded-md shadow-sm border-gray-300 focus:border-indigo-300 focus:ring focus:ring-indigo-200 focus:ring-opacity-50">
</div>
<div class="flex items-center justify-between mt-4">
<div class="relative my-4">
<button type="submit"
class="w-full mx-auto text-white bg-sky-500 border-0 py-2 px-8 focus:outline-none hover:bg-sky-600 rounded text-lg tracking-wider">ログイン</button>
</div>
</div>
</form>
</x-guest-layout>
QRコードを、スマホにインストールしたGoogleアプリで読み込みます。
アプリを立ち上げ、右下の+ボタンを押します。
QRコードをスキャンとでるので押し、カメラで読み込みます。
するとサイト名(メールアドレス)という表示の下に、ワンタイムパスワードが表示されるので、それをフォームに入力します。
通常のログイン画面→成功→QRコードのあるパスワード画面に移行します。
ここで成功するとダッシュボード画面に移行します。
簡単に実装できるので興味のある方はお試しください。
venderの中にpragmarxというフォルダができています。
そのなかのファイルを見ると動きが分かるので、configの設定ファイルと合わせて、カスタマイズも難しくないと思います。
2要素認証の使用の可否をユーザーに決めてもらい、ログイン時に使いたい人だけ使うといった使い方もできます。
Discussion