[Next.js] API Routes と iron-session を使ってカスタムの認証を実装する

[Next.js] API Routes と iron-session を使ってカスタムの認証を実装する

こんにちは、 kenzauros です。

Next.js でユーザー認証といえば、 NextAuth.js がメジャーどころで、私もいくつか記事を書いています。

外部の認証プロバイダーを使う場合などは特に便利ですし、 CredentialsProvider を実装すれば独自の認証を組み込むこともできます。

ただ「独自の認証」だけでいい場合は少し充実しすぎな感もあります。そこで今回は公式の “Authentication” で紹介されている iron-session を用いた方法 を試してみました。

ちなみに公式でも下記のような使い分けがおすすめされています。

If you want a low-level, encrypted, and stateless session utility use iron-session.

低レベルで暗号化されていてステートレスなセッションなら iron-session

If you want a full-featured authentication system with built-in providers (Google, Facebook, GitHub…), JWT, JWE, email/password, magic links and more… use next-auth.

マッチョな機能が全部入りな認証システムをお求めなら next-auth

Authentication | Next.js

iron-session について

iron-session はセッションベースのセッション管理ユーティリティーです。

下記のような特徴があります。

  • Cookie をセキュアに保つことができる
    • HttpOnly, HTTPS のみにできる
    • 暗号化され、サーバー側でのみデコード可能
  • ステートレス
    • セッション ID なし
    • サーバー側でセッション情報管理の必要なし

ステートレスな認可という意味では JWT を用いる方法も一般的ですが、 SPA でない Next.js の場合はセッションを使った認可のほうがわかりやすいかもしれません。

デメリットとしてはクライアント側の Cookie にセッション情報が記録されているため、サーバー側からセッションを無効化する方法がないことでしょうか。

環境

  • Ubuntu 20.02 (Windows 11 Pro WSL2)
  • Node.js 18.12.1
  • React.js 18.0.27
  • Next.js 13.1.2

※ 現時点で Next.js の app Directory は beta のため使用していません。

概要

今回のデモアプリは下記のリポジトリにありますので、参照してください。

ベースは冒頭に紹介した公式サンプルです。

いくらか使いにくい部分があったり、不足している部分があったので、追加・修正しています。

なお、今回はボイラープレートとして使えるよう、認証ロジックをダミーにしています。認証のカスタムロジックは lib/database.tstryLogin を用途に応じて実装することでカスタムできますが、もちろんまったく別の方法で実装しても問題ありません。

ファイル構成

リポジトリのファイル構成は下記のように最小限の構成になっています。

ファイル構成
.
├── lib
│   ├── database.ts
│   ├── fetchJson.ts
│   ├── session.ts
│   └── useAuth.ts
├── pages
│   ├── api
│   │   ├── login.ts
│   │   ├── logout.ts
│   │   └── user.ts
│   ├── _app.tsx
│   ├── _document.tsx
│   ├── index.tsx
│   └── login.tsx
├── next.config.js
├── package-lock.json
├── package.json
└── tsconfig.json

簡単に各ファイルを説明します。

  • lib
    • database.ts
      認証のためのダミー関数 tryLogin を含んでいます。本来は認証のカスタムロジックを実装しますが、関数はダミーでユーザー名 hoge、パスワード hogehoge の場合のみログインできるようになっています。
    • fetchJson.ts
      fetch のラッパーです。サンプルのものをベースに、ヘッダー指定などを内部で行うよう改良しています。
    • session.ts
      iron-session の設定とセッションの型定義、 Next.js の getServerSideProps を生成するための関数を含んでいます。
    • useAuth.ts
      ページで使用する React Hooks です。ログイン中のユーザー情報などのほか、フロントエンドから呼び出すログイン・ログアウト関数を提供します。
  • pages
    • api: API はいずれも lib/useAuth.ts から呼び出します。
      • login.ts: ログイン API です。
      • logout.ts: ログアウト API です。
      • user.ts: セッション情報からユーザー情報を返す API です。
    • _app.tsx
      ユーザー情報の管理に SWR を利用しているため、基本設定をここの SWRConfig で行っています。
    • index.tsx
      ログイン後に表示されるトップページです。
    • login.tsx
      ログイン画面です。

実行

npm run dev で実行すると http://localhost:3000/login でログイン画面が表示されます。

ユーザー名 hoge、パスワード hogehoge でログインできます。

ログイン画面

ログイン画面

ユーザー名かパスワードを間違うとログインに失敗し、 Authentication failed が表示されます。

ログイン失敗

ログイン失敗

ログインすると / にリダイレクトされます。ログイン中のユーザー名とログアウトボタンが表示されます。

ログイン済の index ページ

ログイン済の index ページ

ログアウトボタンをクリックするとログアウトしてログイン画面にリダイレクトされます。

ログアウト後のログイン画面

ログアウト後のログイン画面

大まかな処理の流れ

ログイン処理

ログインは下記のような流れになります。

ログイン処理の流れ

ログイン処理の流れ

ページ表示

ページ表示時のサーバーサイド (SSR) の処理は下記のようになります。

ページ表示の流れ (サーバーサイド)

ページ表示の流れ(サーバーサイド)

SG (Static Generation) でフロントエンド側からユーザー情報が必要な場合は API Route の /api/user から SWR で取得したユーザー情報を利用します。 useAuthuser を参照します。

ページ表示の流れ (フロントエンド)

ページ表示の流れ(フロントエンド)

ログアウト処理

ログアウトはデータベースリクエストがないだけで、ログインとほぼ同様の流れです。 API 内でセッションを破棄すれば完了です。

ログアウト処理の流れ

ログアウト処理の流れ

その他のページの実装

API

API Route の場合、通常通り pages/api 以下にハンドラー設定した ApiRoute を配置します。

iron-session がもつ withIronSessionApiRoute でラップすれば、 req.session でユーザー情報を含むセッション情報を参照できます。

/api/user を参考にするとよいでしょう。

ページ

こちらも Next.js のお作法通り、 pages 以下に tsx ファイルを配置すれば OK です。

こちらは lib/session で定義している withUserSessionSsr を使うことで未認証時のリダイレクトを書かなくてよくなります。

getServerSidePropsの生成
export const getServerSideProps = withUserSessionSsr("/login");

SSR でサーバーサイドの処理が必要な場合は第2引数にハンドラー関数を設定します。セッション情報は req.session から参照できます。

追加のハンドラーを指定してgetServerSidePropsを生成
export const getServerSideProps = withUserSessionSsr("/login", (({ req, res }) => {
  const user = req.session.user;
  // なんらかの処理
  return {
    props: {
      user,
    },
  };
}));

まとめ

今回は Next.js で NextAuth.js を使わず、 API Routes と iron-session を使ってカスタムの認証を実装する方法を紹介しました。

認証部分は用途に応じて実装が必要ですが、ボイラープレートとして使えると思います。

どなたかのお役に立てば幸いです。

kenzauros