Middleware の動かし方
プロジェクトのルート (src
ディレクトリなど) に middleware.ts
ファイルを作成して、ミドルウェアをエクスポートします。あとは Vercel などのサーバーにデプロイすることで、CDN の Edge 上でミドルウェアが動作します。
NextAuth.js withAuth
というミドルウェアを提供しているため、そのまま利用するだけでサイト内のページを保護できます。
export { default } from 'next-auth/middleware';
// `/protected` 以下のリソースには、認証されたユーザーのみアクセスできる。
export const config = {
matcher: ['/protected/(.*)'],
};
注目していただきたいのは config
オブジェクトの部分です。matcher
に正規表現を指定することで、認証を要求するページを限定しています。
特定のページのみ、認証なしでアクセスを許可する
今回やりたかったことは、上記の例とは逆に特定のページだけアクセスを許可することです。方法は公式ドキュメントで紹介されていました。
どうやら matcher の判定には path-to-regexp というライブラリを使用している様なので、その実行結果を検証することで意図した正規表現が組めているか確認できそうです。
正規表現をカスタマイズする
検証しながら正規表現をカスタマイズするため、Jest で path-to-regexp
を動かしてみます。
import { pathToRegexp } from 'path-to-regexp';
test('regexp for matcher in middleware.ts', () => {
// パスと期待値のリスト
// test が true となれば、そのページでミドルウェアが実行される。
const expected = [
{ path: '/', test: false },
{ path: '/about', test: false },
{ path: '/protected', test: true },
{ path: '/protected/foo', test: true },
];
// matcher に使う正規表現
const regexp = pathToRegexp('/protected(/?.*)');
const paths = expected.map(({ path }) => path);
const result = paths.map((path) => ({
path,
test: regexp.test(path),
}));
expect(result).toEqual(expected);
});
テストが通っているので、少なくともリストに挙げたページではミドルウェアが期待通りに実行される (されない) ことが保証されます。続いて、特定のページを除いてミドルウェアを実行する正規表現を検証します。
import { pathToRegexp } from 'path-to-regexp';
test('regexp for matcher in middleware.ts', () => {
const expected = [
{ path: '/', test: true },
{ path: '/protected', test: true },
{ path: '/articles/foo', test: true },
{ path: '/articles/published', test: false },
{ path: '/articles/published/bar', test: false },
];
// `articles/published` 以下のページにはマッチしない正規表現
const regexp = pathToRegexp('/((?!articles/published).*)');
const paths = expected.map(({ path }) => path);
const result = paths.map((path) => ({
path,
test: regexp.test(path),
}));
expect(result).toEqual(expected);
});
正規表現の (?!...)
の部分は negative lookahead assertion という構文を使用しています。上記の例でいうと、絶対パス /
を検索しますが articles/published
が後ろに続くとマッチしません。
おわりに
今回はミドルウェアを実行するページをコントロールする方法を紹介しました。対象のとなるページはこのような方法で検証するとよいかもしれません。