## 概要
この記事は、Laravel+Nuxtでsanctum認証を実装時の自分用の備忘録になります。
環境
- mac M1
- Laravel:8.63.0
- Laravel Sanctum:2.11
- Nuxt:2.15.8
インストール(Laravel)
作業ディレクトリを適当な場所に作成する。
mkdir laravel-nuxt
Laravel プロジェクトの作成
特にバージョンにこだわりなかったので、最新の8系をインストール。
composer create-project laravel/laravel
sanctum のインストール
composer require laravel/sanctum
php artisan vendor:publish --provider="Laravel\Sanctum\SanctumServiceProvider"
database の準備
手軽に確認できるためsqliteを使用しました。
環境に合わせて適宜修正してください。
touch database/database.sqlite
.env ファイル修正
DB_CONNECTION=sqlite // 修正
# DB_HOST=127.0.0.1
# DB_PORT=3306
# DB_DATABASE=laravel
# DB_USERNAME=root
# DB_PASSWORD=
ログイン実装で使用するUserテーブルを作成したいため、migrationを実行します。
php artisan migrate
Cookie 認証
Sancatum のミドルウェアをapp/Http/Kernel.php
のapi
グループに追加します。
これにより Laravel のセッションクッキーを使用して認証することができます。
class Kernel extends HttpKernel
{
protected $middlewareGroups = [
'api' => [
\Laravel\Sanctum\Http\Middleware\EnsureFrontendRequestsAreStateful::class, // 追記
'throttle:api',
\Illuminate\Routing\Middleware\SubstituteBindings::class,
],
];
}
CORS とクッキー
別のサブドメインで実行する場合Access-Control-Allow-Credentials
ヘッダをtrue
で返す必要があるため、以下を設定します。
'supports_credentials' => true,
SPA 認証の実装
SPA 認証するには CSRF 保護を初期化し、ログイン処理をする必要があります。
CSRF 保護を初期化するには/sanctum/csrf-cookie
エンドポイントにリクエストを送信する必要があるので注意しましょう。
axios.get('/sanctum/csrf-cookie').then(response => {
// ログイン…
});
またログインやログアウト処理は Laravel の公式曰く'web'ルートに作成する必要がある。
Laravel 8.x Laravel Sanctum
ログインコントローラーの作成を行う。
php artisan make:controller LoginController
ルーティングの設定を行う。
<?php
use App\Http\Controllers\LoginController;
Route::post('/login', [LoginController::class, 'login']);
Route::post('/logout', [LoginController::class, 'logout']);
config/cors.php
にlogin
とlogout
のパスを追加。
'paths' => [
'api/*',
'login', // 追加
'logout', // 追加
'sanctum/csrf-cookie',
],
LoginController
の実装をする。公式のユーザーを手動で認証する
のコードの return を少し変更します。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Auth;
use SebastianBergmann\Environment\Console;
class LoginController extends Controller
{
/**
* Handle an authentication attempt.
*
* @param \Illuminate\Http\Request $request
* @return \Illuminate\Http\Response
*/
public function login(Request $request)
{
$credentials = $request->validate([
'email' => ['required', 'email'],
'password' => ['required'],
]);
if (Auth::attempt($credentials)) {
$request->session()->regenerate();
return response()->json(Auth::user());
}
return response()->json(['message' => 'ログインに失敗しました'], 401);
}
public function logout(Request $request)
{
Auth::logout();
$request->session()->invalidate();
$request->session()->regenerateToken();
return response()->json(['message' => 'ログアウトしました']);
}
}
またapi.php
のコードを一部修正します。
use Illuminate\Support\Facades\Auth;
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return Auth::user(); //修正箇所
});
クッキー認証の実装は完了です。次にテスト用のユーザを作成します。
$ php artisan tinker
App\Models\User::factory()->create(['email' => 'hoge@example.com']);
exit
Laravel8 系のデフォルトパスワードはpassword
です。
Laravel の実装はこれで終了です。
次に Nuxt 側の実装を行っていきます。
インストール(Nuxt)
Nuxt プロジェクトの作成
yarn create nuxt-app nuxt
初期設定はSSR
設定とaxios
モジュールを選択します。あとはお好みで選択してください。
axios で CORS の設定
nuxt.config.js
に以下を追記します。
これによってaxios
のwithCredentials
オプションをtrue
にします。
axios: {
baseURL: 'http://localhost:8000',
credentials: true,
},
ログインした管理ユーザーを保存する Store を作成する。
ログインユーザーを管理するため、store/auth.js
を作成します。
ログインの処理をする前に/sanctum/csrf-cookie
にアクセスし,CSRF 保護を初期化する必要がありますので注意してください。
export const state = () => ({
authUser: null,
})
export const mutations = {
setAuthUser(state, authUser) {
state.authUser = authUser
},
}
export const actions = {
async login({ commit }, { email, password }) {
await this.$axios.get('/sanctum/csrf-cookie').then(async (res) => {
const response = await this.$axios
.$post('/login', { email, password })
.catch((err) => {
console.log(err)
})
commit('setAuthUser', response)
})
},
async logout({ commit }) {
await this.$axios
.$post('/logout')
.then((res) => {
console.log(res)
})
.catch((err) => {
console.log(err)
})
commit('setAuthUser', null)
},
}
API からログイン中の管理者ユーザを取得するようにする
Store のデータはリロードすると消えてしまうため、リロードする度にログインする必要があります。
そのため画面表示前にログイン中のユーザー情報を取得して Store にセットする処理が必要になります。
SSR から Cookie を取得するためにcookieparser
モジュールを追加する。
yarn add cookieparser
store/index.js
を追加し、以下を記述する。
const cookieparser = process.server ? require('cookieparser') : undefined
export const actions = {
async nuxtServerInit({ commit }, { req, app }) {
cookieparser.parse(req.headers.cookie)
await app.$axios
.$get('/api/user')
.then((authUser) => {
commit('auth/setAuthUser', authUser)
})
.catch((err) => {
console.log(err)
commit('auth/setAuthUser', null)
})
},
}
nuxtServerInit は SSR 時に実行される処理で、
Cookie が有効な間はログイン中の管理者ユーザーの情報が毎回セットされるようになります。
ログイン中かをチェックして画面遷移させるミドルウェアを作成する
middleware
配下にunAuthenticated.js
を作成します。
これにより Store に state.auth.authUser
に設定されていれば、管理画面に遷移させます。
export default function ({ store, redirect }) {
if (store.state.auth.authUser) {
return redirect('/admin')
}
}
middleware
配下にauthenticated.js
を作成し
これにより Store にstate.auth.authUser
に設定されていなければ、ログイン画面に遷移します。
export default function ({ store, redirect }) {
if (!store.state.auth.authUser) {
return redirect('/admin/login')
}
}
ログイン後の画面を作成する
pages/admin/index.vue
ファイルを作成する。
<template>
<section>
<h1>管理画面へようこそ</h1>
<button @click="logout">ログアウト</button>
</section>
</template>
<script>
export default {
middleware: 'authenticated',
methods: {
async logout() {
await this.$store.dispatch('auth/logout', null)
this.$router.push('/admin/login')
},
},
}
</script>
ログイン画面を作成する
pages/admin/login.vue
を作成する。
<template>
<section>
<h1>ログイン</h1>
<form @submit.prevent="submit">
<div>
<label for="email">email</label>
<input type="text" id="email" v-model="email" />
</div>
<div>
<label for="password">password</label>
<input type="password" id="password" v-model="password" />
</div>
<button type="submit">login</button>
</form>
</section>
</template>
<script>
export default {
middleware: 'unAuthenticated',
data() {
return {
email: '',
password: '',
}
},
methods: {
async submit() {
await this.$store.dispatch('auth/login', {
email: this.email,
password: this.password,
})
this.$router.push('/admin')
},
},
}
</script>
動作確認
アプリを起動して、動作確認します。
まずLaravel(API)サーバーを起動します。
php artisan serve
次に Nuxt アプリを起動する。
yarn run dev
起動が完了後、http://localhost:3000/admin にアクセスします。
下記のような画面が表示されると思います。
次に事前に登録して管理者ユーザーのメールアドレスとパスワードを入力してログインします。
ログインが成功し、/adminにリダイレクトされ下記の画面が表示されればOKです。
またログイン後にリロードしてもStoreにログインユーザが設定され管理画面から遷移されないことを確認します。
ログアウトボタンを押下し、ログイン画面まで遷移されればOKです。