お問い合わせ・ご相談についてはこちら

Next.js 13 middlewareでトークン認証を実装した事を振り返る[firebase]

プログラミング
この記事は約7分で読めます。

こんにちは。
今回は、
前回の記事の続きにあたります。

前回の記事では、
firebaseのAuthを使って
認証を得たユーザーのみページにアクセスできるように
ページブロック機能を実装する方法を、
セキュリティガン無視の入り口として書きました。

ただ、
それではセキュリティ対策が出来ていないので、
本記事では「トークン認証」を実装していきます。

よろしくお願いします。

今回も前提として、
firebase authenticationと、
Next.js 13 の使用を前提としています。

firebaseのauthの認証の仕組み

簡単にfirebase認証の仕組みを説明します。

例えば、
firebaseで上記事の途中に説明した通り、
ユーザーがfirebase authを使ってログインをしたとします。

すると、
firebase内で「トークン」と呼ばれるログインした人専用の鍵が発行されます。

このトークンはクライアントサイドでFirebase SDKを使って取得でき、
取得したトークンをサーバーサイドのFirebase Admin SDKを使用して
認証することが出来ます。

Next.js 13ではどこに認証コードを書くべきか。

Next.js 13から、
クライアントサイドコンポーネントとサーバーサイドコンポーネント
の区別がはっきりしました。

じゃあ認証コードをどこに書けばいいんじゃい。ということで、
まずは認証を「コンポーネント(サーバーorクライアント)」に書くことを考えます。

Rendering: Server Components | Next.js
Learn how you can use React Server Components to render parts of your application on the server.

しかし、
例えば認証用コンポーネントを作ってpage.tsxなどに記述したとき、
他のコンポーネントが先に表示されてから、
認証用コンポーネントが実行し終わり、
ちょっとだけページの内容が見えてしまうということが起きる可能性があります。

例えば、<LoginListener>コンポーネントで
全ての認証を行おうとすると、
先にHeaderやchildrenが表示されてしまい、
<LoginListener>が実行し終わるとページから弾かれるという動きになります。

これだと、
ログインしていないユーザーが中身を見えてしまうので避けたいところです。

そこで今回注目するのが、
middlewareファイルです。

Next.js 13 middlewareファイルとは何か。

Next.js 13においてmiddlewareは、
コンポーネントのマウント前に実行します。

そのため、
ページブロックといった認証に相性がいいファイルです。

ここで認証をすれば、
ユーザーのリクエスト→middlewareの実行→コンポーネントのマウント
の順の実行なので、
遷移先のページが誰でも一瞬見えてしまうことを避けます。

コンポーネントのマウントより先に実行をする点、
この機能を使わない手はありませんね。
認証コードはmiddlewareにコードを書くので良さそうです。

しかし、
middlewareの立ち位置を考えると、
このファイルはサーバーサイドでもクライアントサイドでもなく、
実行言語も異なるため、
本当に「その間にあるファイル」という認識の方が正しいようで、
少し問題が生じます。

実際に、
middlewareでfirebase-admin SDKを使用を試みると、
firebase-admin SDKはサーバーサイドでの使用を想定しているため
使えませんとエラーが出てしまいます。

以上を踏まえて方針は、
middlewareに認証コードは書きつつ、
firebase-admin SDKが使えない問題をどう解決するかを考えていくことにします。

middlewareについては
このサイトが分かりやすかったです。

Next.jsでの初心者向け Middleware.ts入門 (v13.1.0) + 公式マニュアル解説 - Qiita
TODOi18n認証対象Middlewareって何?どんな事ができるの?どんな機能があるの?と疑問を持っている人。切り貼りコピペで使っていた人(=自分)向け。Middleware …
Routing: Middleware | Next.js
Learn how to use Middleware to run code before a request is completed.

トークン認証の流れ

以上を踏まえて、
トークン認証の理論を出します。

流れとしては、
大まかに2ブロックあります。
赤がクライアントコンポーネントの実装。
青がmiddlewareの実装です。

まず第一に、
「ユーザーがログインをしたら、トークンを取得してcokkieに保存する」
です。

ユーザーがログインをしたら
firebaseからトークンが発行されるので、
ユーザーがログイン、ログアウトをするタイミングで、
そのトークンを取得しておいて、
cookieのようなcashに保存しておきます。

これは
ユーザーがログインをしたらuseAuthStateの値が変わるので、
それを機にuseEffectを発火させて
トークンの取得とcookieへの設置を考えればいいですね。

以上は、クライアントサイドコンポーネントで
LoginListenerなどの名前で実装します。
(コンポーネントの前にuse clientとかを書いて、
returnはnullとかにすればいいですな。)

cookieの設置に関しては、
クロスサイトスクリプティング攻撃も考えて、
HttpOnlyなどの対策をする必要があります。

そして第二に、
「middlewareでcookieからtokenを取得してFirebase-admin SDKで認証する」

上のクライアントコンポーネントによって、
cookieにトークンは保存されたので、
ユーザーがページ遷移したいってリクエストを
サーバーに送る際、
そのリクエストにtokenも乗るわけです。

middlewareでtokenを取得(cookieから)することが可能。

あとはfirebase-admin SDKを使用して認証するだけですが、
先ほど話した通り、
middlewareでfirebase-admin SDKは使用出来ません。

これは、
middlewareでapiを呼び出して、
サーバーサイドでfirebase-admin SDKによる認証を行ってもらい、
結果を返してもらうことで、
実装出来ます。

そして最後に、
その認証結果によって、
ページリダイレクトを行えば、
ログインしていないユーザーをページブロックすることが可能です。

こうして、
ログインしているユーザーはそのままページにアクセスすることが出来、
そうでないユーザーはログインページなどにリダイレクト
させることが出来ます。

この認証はページ遷移速度を遅くしていないか。

以前こんな記事を書きました。

この記事から分かる通り、
next.js13では初めて新しいページに遷移するとき、
大体はサーバーを一度経由します。

(一度訪れたページに再度戻ってくる場合はその限りではない)

なので、
Next.js13はプロジェクトの後半の方が、
クライアントサイドのページキャッシュが溜まるので、
ページ遷移速度が速くなると思いますが、

その恩恵は、受けられません。
しかし、
最初の方は、結局サーバーを経由しているので、
middlewareでapiを使う(サーバーを経由する)ことは、
必要最低限の遅延と考えられます。

コメント

タイトルとURLをコピーしました