【Next.js】SSG(ISR)でも簡単にベーシック認証やverifyIdTokenを行う方法

2022-04-16

Middleware を使用すれば意外とあっさり組めちゃいます。

import fetchAdapter from "@vespaiach/axios-fetch-adapter";
import axios from "axios";
import { NextRequest, NextResponse } from "next/server";

async function middleware(req: NextRequest): Promise<Response> {
  // revalidate 時またはファイルを参照している場合は全通しする
  if (
    req.headers.has("x-prerender-revalidate") ||
    req.nextUrl.pathname.includes(".")
  ) {
    return NextResponse.next();
  }

  /*
   * ベーシック認証 start
   */
  // api の場合は認証を行わない
  if (!req.nextUrl.pathname.startsWith("/api")) {
    const authRequiredResponse = new Response("Auth required", {
      headers: {
        "WWW-Authenticate": 'Basic realm="Secure Area"',
      },
      status: 401,
    });
    const basicAuth = req.headers.get("authorization");

    if (!basicAuth) {
      return authRequiredResponse;
    }

    const auth = basicAuth.split(" ")[1];
    const [user, password] = Buffer.from(auth, "base64").toString().split(":");

    if (
      password !== process.env.BASIC_AUTH_PASSWORD ||
      user !== process.env.BASIC_AUTH_USER
    ) {
      return authRequiredResponse;
    }
  }
  /*
   * ベーシック認証 end
   */

  /*
   * verifyIdToken start
   */
  // ログイン不要なページは verify を行わない
  if (
    req.nextUrl.pathname !== "/redirect" &&
    req.nextUrl.pathname !== "/signin" &&
    req.nextUrl.pathname !== "/signout"
  ) {
    try {
      const axiosInstance = axios.create({
        adapter: fetchAdapter,
      });

      await axiosInstance.get(`${req.nextUrl.origin}/api/verifyIdToken`, {
        headers: {
          cookie: `idToken=${req.cookies.idToken}; refreshToken=${req.cookies.refreshToken}`,
        },
      });

      return NextResponse.next();
    } catch {
      return NextResponse.redirect(`${req.nextUrl.origin}/signout`);
    }
  }
  /*
   * verifyIdToken end
   */

  return NextResponse.next();
}

export default middleware;

ポイントは req.headers.has("x-prerender-revalidate") です。

この実装によって revalidate 時に認証を回避することが可能となります。Failed to revalidate route of a specific locale

ぜひ参考になりましたら幸いです。