【Node.js】期限をクライアントでチェックしてJWTトークンリフレッシュ

こんにちは、フリーランスエンジニアの太田雅昭です。

トークンリフレッシュ

アクセストークンとリフレッシュトークンを使用して、認証を実装するのは今の一般的な手法です。このメリットは以下のようなものです。

メリット

  • リフレッシュトークンをアクセストークンよりセキュアに管理することで、セキュリティがアップする。
  • リフレッシュトークンIDはサーバーで管理することで、不正使用に対する防御ができる。

一般的な更新方法

よくインターネットで検索して出てくる更新方法は、以下のものが多いです。

  • 通常通りAPIにアクセス
  • アクセストークン切れ (401エラー) が発生したら、トークンをリフレッシュし、再トライ

ただこれだと、色々困ることも出てきます。たとえば、

  • 使用しているライブラリによっては、実装が複雑になったり、あるいは不可能だったりする。
  • 一度401エラーを発生させるアクセスの分、若干パフォーマンスが落ちる。

クライアントで期限をチェックし、リフレッシュする方法

そういうわけで、場合によってはクライアント側で期限をチェックする方法が有効です。具体的には以下のように、JWTをデコードして期限をチェックし、期限切れ、あるいは切れそうならリフレッシュトークンを更新するというものです。


export async function checkAndRefreshToken() {
    const ADDITIONAL_MSEC = 60 * 1000;
    const exp = getAccessTokenExp();

    if (!exp || Date.now() < exp - ADDITIONAL_MSEC) {
        return;
    }

    await doRefreshToken();
}

function getAccessTokenExp() {
    const token = getAccessToken();
    const exp = decodeToken(token)?.exp * 1000; // トークンの有効期限を取得
    return exp;
}

function decodeToken(token: string | undefined): Record<string, any> | null {
    if (!token) return null;
    const base64Url = token.split('.')[1]; // JWTのペイロード部分を取得
    const base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
    const jsonPayload = decodeURIComponent(
        atob(base64)
            .split('')
            .map((c) => {
                return '%' + ('00' + c.charCodeAt(0).toString(16)).slice(-2);
            })
            .join('')
    );

    const result = JSON.parse(jsonPayload);
    return result;
}

これで、簡単にトークンリフレッシュが実装できました。

小話

昔流行っていた頃はあまり興味がなかったのですが、今シェリル・クロウをよく聞いてます。メロディーも歌声もいいですね。