Astroのミドルウェアを使ってBasic認証をかける方法

Astroのミドルウェアを使ってBasic認証をかける方法をまとめました。

ミドルウェアとは

ミドルウェアは、リクエストとレスポンスの間に割り込む処理のことです。
リクエストを受け取り、割り込んで処理を行った後に、次のミドルウェアやレンダリング処理に渡す役割を持ちます。

通常、ユーザーのリクエストからレスポンスまでの流れは以下のような流れで処理が行われます。

ブラウザ → リクエスト → サーバー → レスポンス → ブラウザ

ミドルウェアを使用することでリクエストとレスポンスの間に介入し、リクエストに応じた処理を行うことができます。

ブラウザ → リクエスト → [Middleware] → サーバー → レスポンス → ブラウザ

メモ

ソフトウェアの文脈でもミドルウェアという言葉があるようです。 こちらはOSとアプリケーションの間で橋渡しをする役割を持っているようでなので、混同しないように注意が必要そうです。 Astroなどのフレームワークの文脈ではリクエストを処理するための関数であり、OSとアプリケーションの間で橋渡しをする役割を持っているわけではありません。

ミドルウェアで何ができるのか

具体的にはミドルウェアを使用することで、以下のようなことが実現できます。

  • 認証
  • キャッシュ制御
  • リダイレクト
  • ヘッダーやクッキーの操作

などが挙げられます。

ミドルウェアの基本的な使用方法

ミドルウェアは、src/middleware.jsまたはsrc/middleware.tsを作成して記述します。

import { defineMiddleware } from "astro:middleware";
export const onRequest = defineMiddleware((context, next) => {
// ここに処理を記述します。
});

defineMiddlewareはAstroのミドルウェアを定義するための関数です。
contextはリクエストの情報を含むオブジェクトで、nextは次のミドルウェアまたはレンダリング処理に渡すための関数です。 このユーティリティ関数を使用すると、引数contextnextは自動で型付けされます。

公式ドキュメント - defineMiddleware()

middlewareを使ってBasic認証をかけてみる

Basic認証の処理の流れ

Basic認証は、ユーザー名とパスワードを入力して認証を行う認証方式です。
Basic認証の処理の流れは以下のようになります。

  1. ブラウザでURLにアクセスする(リクエスト)
  2. サーバーが認証が必要と返す(401 + WWW-Authenticateヘッダー)
  3. ブラウザでIDとパスワードの入力を求めるダイアログが表示される
  4. ユーザーがIDとパスワードを入力し、リクエストを送信すると、ブラウザがID/パスワードをBase64エンコードしてリクエストを再送する
  5. サーバーが認証が成功と返す(200)
  6. ブラウザがページを表示する

ミドルウェアでBasic認証をかける

src/middleware.tsを作成して、以下のように記述します。

src/middleware.ts
import { defineMiddleware } from "astro:middleware";
import { Buffer } from "node:buffer";
export const onRequest = defineMiddleware((context, next) => {
/* 認証ヘッダーを取得 */
const authHeader = context.request.headers.get("Authorization");
/* 認証ヘッダーがない場合は401を返す */
if (!authHeader?.startsWith("Basic ")) {
return new Response("認証が必要です", {
status: 401,
headers: {
"WWW-Authenticate": 'Basic realm="Secure Area"',
},
});
}
console.log(authHeader);
/* Base64デコード */
const [id, password] = Buffer.from(
authHeader.slice("Basic ".length),
"base64",
)
.toString("utf-8")
.split(":");
const expectedUser = import.meta.env.BASIC_AUTH_USER;
const expectedPass = import.meta.env.BASIC_AUTH_PASSWORD;
/* 認証に失敗した場合は401を返す */
if (!id || !password || id !== expectedUser || password !== expectedPass) {
return new Response("認証に失敗しました", {
status: 401,
headers: {
"WWW-Authenticate": 'Basic realm="Secure Area"',
},
});
}
return next();
});

index.astroで以下のように記述します。 このような構成でBasic認証をかけたい場合、SSRアダプター(@astrojs/cloudflare など)を使用する必要があります。 prerenderfalseにすることでミドルウェアが適用されます。 ただし、本来静的生成をしたいページでprerendertrueにすることは避けるべきなので、あまり良い手法ではないと言えるでしょう。

index.astro
---
export const prerender = false;
---

.envを作成して、以下のように記述します。

BASIC_AUTH_USER=your_username
BASIC_AUTH_PASSWORD=your_password

実際にローカルで動作を確認するために、pnpm devを実行して、ブラウザでhttp://localhost:4321にアクセスします。 Basic認証のダイアログが表示され、認証が成功するとページが表示されました。

Buffer.fromとは?

Buffer.from

は、Node.jsのAPIの一つで、文字列をバッファに変換するためのメソッドです。 バッファとはデータを一時的に保存するメモリ上の領域のことです。 Base64エンコードされた文字列をバイト列として読み込んで、それをUTF-8の文字列に変換します。

参考

一覧に戻る