🐡
(JWT)トークン満了後、自動ログアウト機能を実装
ログイン機能ではJWTのトークン発行まで実装していましたが、トークンが切れてもログアウトされないことに気づきました。
トークンが切れるとサーバーから401エラーが返ってくるため、そのエラーを利用してログアウト処理を追加します。
JWTトークンの有効期限チェックユーティリティ関数を作る
JWTトークンには、有効期限を表す exp
フィールドが含まれています。
この期限をチェックし、トークンが切れているかどうかを判定するユーティリティ関数を作ることで、クライアント側でトークンの有効性を簡単に管理できます。
// src/utills/jwt.ts
// ターミナルで設置:npm install jwt-decode
import jwt from "jsonwebtoken";
export function isTokenExpired(token: string){
try{
const decodedToken = jwt.decode(token) as { exp: number };
if (!decodedToken || !decodedToken.exp) {
return true; // トークンの有効期限が不明な場合は、期限切れとみなす
}
const now = Date.now() / 1000; // 現在のUNIXタイムスタンプ(秒)
return decodedToken.exp < now; // トークンの有効期限が過ぎているかどうかをチェック
}catch(err){
console.error("Error in isTokenExpired", err);
return true; // エラーが発生した場合は、期限切れとみなす
}
}
JWTトークンの有効期限チェック後、自動ログアウト処理
トークンが切れていたら、localStorage
から削除し、ログインページへリダイレクトします。
// src/hooks/useAutoLogout.ts
import { useRouter } from "next/router";
import { useEffect } from "react";
import { isTokenExpired } from "../utils/jwt";
export default function useAutoLogout(token : string | null) {
const router = useRouter();
useEffect(()=>{
if (!token){
return; // トークンがない場合は何もしない
}
if(isTokenExpired(token)){ // トークンが期限切れの場合
localStorage.removeItem("token");
alert("Session expired. Please log in again.");
router.push("/login");
}
},[router,token]);
}
グローバルページでログアウト処理を実行する
現在のグローバルページは /src/app/layout.tsx
です。ここでトークンの有効期限をチェックして、自動的にログアウトさせる処理を行いたいと考えました。
しかし、/src/app/layout.tsx
はサーバーコンポーネントなので、useEffect
や useState
などのクライアント専用フックを直接使うことができません。
そのため、クライアントコンポーネントを別途作成して、グローバルレイアウトでラップします。
AuthGuard クライアントコンポーネント
// src/components/AuthGuard.tsx
"use client";
import useAutoLogout from "@/hooks/useAutoLogout";
import { useEffect, useState } from "react";
export default function AuthGuard({ children }: { children: React.ReactNode }) {
const [token, setToken] = useState<string | null>(null);
useEffect(()=>{
// ローカルストレージからトークンを取得
const storedToken = localStorage.getItem("token")
if (!storedToken){
setToken(null);
return;
}
// トークンが存在する場合は、stateにセット
setToken(storedToken);
},[])
// トークンが変更された場合に自動ログアウトを実行
useAutoLogout(token);
return(
<>{children}</>
)
}
layout.tsx で適用例
// app/layout.tsx
import AuthGuard from "@/components/AuthGuard";
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang="en">
<body>
<AuthGuard>{children}</AuthGuard>
</body>
</html>
);
}
Discussion