本記事で利用したパッケージ
- Firebase for Laravel
https://github.com/kreait/laravel-firebase#support
上記の設定などは、各自README通りにお願い致します。
主に、Authentication
を利用しています。
→Authenticationの利用方法(https://firebase-php.readthedocs.io/en/stable/authentication.html)
*自分は、composerを利用しました。
本記事の目標
- FirebaseのAuthenticationの情報をLaravelで利用できる
- Firebase x Laravel でログイン機能の実装ができる。
- FirebaseのJWTトークンをフロントのHeaderから受け取り、middleware上でデコード、認証しFirebaseのuidとemailを取得できる。
- 取得したuidとemailが、他のController上で利用できるようにする。(ログイン認証したユーザーだけが動かせるロジックを組み込みため)
- Input整理のためのOutput
実装
まず、.envファイルにある設定を組み込みます。
GOOGLE_CLOUD_PROJECT=XXXXXXXXX
上記の記事に記載されている通り、GOOGLE_CLOUD_PROJECTに利用しているFirebase Authenticationのプロジェクト名を記載する。
→これにより、Googleの公開鍵を取得し、認証情報を取得します。
今回は、middlewareで実装し、Controller内で取得した値を利用できるように実装します。
middlewareの設定をします。
protected $routeMiddleware = [
'auth' => \App\Http\Middleware\Authenticate::class,
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
'cache.headers' => \Illuminate\Http\Middleware\SetCacheHeaders::class,
'can' => \Illuminate\Auth\Middleware\Authorize::class,
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
'password.confirm' => \Illuminate\Auth\Middleware\RequirePassword::class,
'signed' => \Illuminate\Routing\Middleware\ValidateSignature::class,
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
'verified' => \Illuminate\Auth\Middleware\EnsureEmailIsVerified::class,
'firebase' => \App\Http\Middleware\Firebase::class,
];
'firebase' => \App\Http\Middleware\Firebase::class,
でmiddlewareの設定場所を定義します。
<?php
namespace App\Http\Middleware;
use Kreait\Firebase\Contract\Auth;
use Kreait\Firebase\Exception\Auth\FailedToVerifyToken;
class Firebase
{
private Auth $auth;
public function __construct(Auth $auth)
{
$this->auth = $auth;
}
public function handle($request, \Closure $next)
{
$idTokenString = $request->headers->get('authorization');
$token = trim(str_replace('Bearer', '', $idTokenString));
try {
$verifiedIdToken = $this->auth->verifyIdToken($token);
} catch (FailedToVerifyToken $e) {
echo 'The token is invalid: '.$e->getMessage();
}
$firebaseId = $verifiedIdToken->claims()->get('sub');
$email = $verifiedIdToken->claims()->get('email');
$request->merge([
'firebaseId' => $firebaseId,
'email' => $email,
]);
return $next($request);
}
}
Kreait\Firebase\Contract\Auth
こちらの使い方は、パッケージのドキュメントで確認してください。
今回は、フロントから送られてくるHeader内にJWTトークンを含ませています。
まずは、JWTトークンを取得するところから
$idTokenString = $request->headers->get('authorization');
Keyは、authorization
になっており、Valueが、JWTトークンです。
try {
$verifiedIdToken = $this->auth->verifyIdToken($token);
} catch (FailedToVerifyToken $e) {
echo 'The token is invalid: '.$e->getMessage();
}
上記は、verifyIdToken()
メソッドを使用してIDトークンを確認します。
トークンは、1時間で有効期限が切れます。
$firebaseId = $verifiedIdToken->claims()->get('sub');
$email = $verifiedIdToken->claims()->get('email');
トークン確認が終わり、認証済みのトークンに切り替わり?ます。
(トークンをデコードしただけらしい?)
このトークンからuid
, email
などユーザー情報を取得することができます。
上記は、ユーザー情報を取得してきています。
$request->merge([
'firebaseId' => $firebaseId,
'email' => $email,
]);
return $next($request);
あとは、取得した値をControllerに渡す際に、requestに混ぜてしまいます。
(セキュリティ的になんともいえなさそうな雰囲気を感じます。)→コメントでご教授願います。
では、ルーティングの設定を行います。
middlewareの処理をControllerに入る前に挟むかどうかを決めてます。
<?php
use Illuminate\Http\Request;
use Illuminate\Support\Facades\Route;
use App\Http\Controllers\UserController;
/*
|--------------------------------------------------------------------------
| API Routes
|--------------------------------------------------------------------------
|
| Here is where you can register API routes for your application. These
| routes are loaded by the RouteServiceProvider within a group which
| is assigned the "api" middleware group. Enjoy building your API!
|
*/
Route::middleware('auth:sanctum')->get('/user', function (Request $request) {
return $request->user();
});
Route::middleware(['firebase'])->group(function (){
Route::post('/user', UserController::class);
});
group内のみmiddlewareのfirebase
が適用されています。
UseControllerの実装前にmiddlewareのfirebase.phpが実装されている感じになります。
<?php
namespace App\Http\Controllers;
use Illuminate\Http\JsonResponse;
use Illuminate\Http\Request;
use Symfony\Component\HttpFoundation\Response;
class UserController extends Controller
{
public function __construct()
{
$this->middleware('firebase');
}
public function __invoke(Request $request): JsonResponse
{
$firebaseId = $request->firebaseId;
$email = $request->email;
var_dump($firebaseId);
var_dump($email);
$response = [
'firebase_id' => $firebaseId,
'email' => $email,
];
var_dump($response);
return response()->json($response, Response::HTTP_CREATED);
}
}
$requestの中に値を入れているので、上記のようなにすれば値を取得できます。
var_dump
で値の中身を見てみましょう!
当たり前かと思いますが、responseに含めることも可能です!
つまり、ルーティングのgroupに入れてしまえば、認証済みユーザーの情報をController内で使用できます。
まとめ
middlewareでController前にログインの認証(やっていることはほぼデコードであるが、)を行えるようにしたい!ってなったけど、他に記事的な物がなかったのでこれは記事にできそう!と思い、書きました。
初学者的には、middlewareに実装の想像もできず、少し苦戦していましたが、徐々に理解して勉強になりました。
一応、クリーンアーキテクチャで実装している時に行ったので、クリーンアーキテクチャでも流用できると思います。(依存の方向が正しいか不安...)
別の方法で、UseCase層で実装している方法も行いました。時間があれば、そちらも記事にしたいと思います。
徐々にOutput用に記事、記載していけたらと思います。
コメントやLGTMなどよろしくお願いします!