📌

VercelにデプロイしているStorybookにBasic認証を設定する

2024/03/02に公開

はじめに

Vercelにデプロイするプロジェクトならどれでも設定できるとは思うのですが、Storybookでしか確認できていないのでこのようなタイトルにしています。

問題

既存のNext.jsプロジェクトではBasic認証が機能していましたがNextResponse()/api/authへリダイレクトしてWWW-Authenticateヘッダー等を返していたりとNext.js専用の実装だったためStorybookでは機能しませんでした。

※実装時に参考にしていたこちらの記事から引用しています。

middreware.ts
import { NextRequest, NextResponse } from "next/server";

export function middleware(req: NextRequest) {
  const basicAuth = req.headers.get("authorization");
  const url = req.nextUrl;

  const BASIC_USER = process.env.BASIC_USER;
  const BASIC_PASSWORD = process.env.BASIC_PASSWORD;

  if (!BASIC_USER || !BASIC_PASSWORD) {
    return NextResponse.next();
  }

  if (basicAuth) {
    const authValue = basicAuth.split(" ")[1];
    const [user, pwd] = Buffer.from(authValue, "base64").toString().split(":");

    if (user === BASIC_USER && pwd === BASIC_PASSWORD) {
      return NextResponse.next();
    }
  }
  url.pathname = "/api/auth";

  return NextResponse.rewrite(url);
}
pages/api/auth.ts
import { NextApiRequest, NextApiResponse } from "next";

export default function handler(req: NextApiRequest, res: NextApiResponse) {
  res.setHeader("WWW-authenticate", 'Basic realm="Secure Area"');
  res.statusCode = 401;
  res.end(`Auth Required.`);
}

解決方法

@vercel/edgeを使ってStorybookにもbasic認証を設定できるようにしていきます。

くわしくはこちらを参考にしてください。

パッケージをインストールします。

npm install @vercel/edge

↓インストール後はこちらを置き換えます。※参考記事はJSでしたので型について記述

- export function middleware(request: NextRequest) {
+ export function middleware(request: Request) {

実際のコード
middlewareからWWW-Authenticateヘッダー等を返すようになっています。

middreware.ts
import { next } from "@vercel/edge";

const USER_NAME = "test";
const PASSWORD = "test";

export const config = {
  matcher: "/",
};

export default function middleware(request: Request) {
  const authorizationHeader = request.headers.get("authorization");

  if (authorizationHeader) {
    const basicAuth = authorizationHeader.split(" ")[1];
    const [user, password] = atob(basicAuth).toString().split(":");

    if (user === USER_NAME && password === PASSWORD) {
      return next();
    }
  }

  return new Response("Basic Auth required", {
    status: 401,
    headers: {
      "WWW-Authenticate": 'Basic realm="Secure Area"',
    },
  });
}

おわりに

本番環境でBasic認証を適用しない等は適宜対応をおこなってください。

参考

Discussion