Next.jsのMiddlewareでverifyIdTokenする方法
2022-04-12
書きっぷりが良いのかどうかわからないですが、とりあえず動いたので備忘録をば。
まずは /pages/_middleware.ts
です。
import fetchAdapter from "@vespaiach/axios-fetch-adapter";
import axios from "axios";
import { NextRequest, NextResponse } from "next/server";
async function middleware(req: NextRequest): Promise<Response> {
const axiosInstance = axios.create({
// これを入れないと axios がたたけない
adapter: fetchAdapter,
});
const {
cookies: { idToken, refreshToken },
nextUrl: { origin, pathname },
} = req;
// TODO: robots.txt などを弾きたい
if (pathname.includes(".")) {
return NextResponse.next();
}
// verify したくないルーティングは先に弾く
// if (
// pathname === "/signin" ||
// pathname === "/signout"
// ) {
// return NextResponse.next();
// }
try {
// get のみ call 可能?
await axiosInstance.get(`${origin}/api/verifyIdToken`, {
headers: {
// TODO: cookie の設定方法はこれで良いのか…?
cookie: `idToken=${idToken}; refreshToken=${refreshToken}`,
},
});
return NextResponse.next();
} catch {
return NextResponse.redirect(`${origin}/signout`);
}
}
export default middleware;
続いて /pages/api/verifyIdToken/index.ts
側です。
import axios from "axios";
import admin from "firebase-admin";
import { parseCookies, setCookie } from "nookies";
import getHandler from "libs/getHandler";
const handler = getHandler();
handler.get(async (req, res) => {
const { idToken, refreshToken } = parseCookies({ req });
try {
await admin.auth().verifyIdToken(idToken);
} catch {
const {
data: { id_token: newIdToken },
} = await axios.post(
`https://securetoken.googleapis.com/v1/token?key=${process.env.NEXT_PUBLIC_FIREBASE_API_KEY}`,
{
grant_type: "refresh_token",
refresh_token: refreshToken,
},
);
setCookie({ res }, "idToken", newIdToken, {
maxAge: 30 * 24 * 60 * 60,
path: "/",
sameSite: "Lax",
});
}
res.status(200);
res.end();
});
export default handler;
最近 next-connect を使って書くのにハマッているのでちょっと変わった実装になってますが、基本部分は何も変わらないと思います。
ずっと前から実装に困っていたので、ひとまず解決できて良かったです。