Learn | Next.js(AppRouter) x NextAuth(v4) x Cognito x Custom Login Page
Next.js(App Router)とNextAuth(v4)での実装事例をあまり見かけなかったので調べた情報を自分なりにまとめる。
※App Routerは採用するけどNextAuthのv5は現時点ではまだbeta版のためv4を使いたい、またサービスのトンマナの関係からログインページはHosted UIではなくカスタムログインページを作成したいという背景
だいたいよくある組み合わせは、
- Next.js(Page Router) x NextAuth(v4)
もしくは - Next.js(App Router) x NextAuth(v5@beta)
が多く、紹介されている記事によって(コード)が若干違ったりするのでややこしかった。
language/framework/package/etc | version/detail |
---|---|
React | ^18 |
Next.js | 14.1.3 |
ルーティング機構 | App Router |
NextAuth | ^4.24.6 |
HostedUIの使用 | ✗(カスタムログインページ作成) |
検証環境のディレクトリ構成はsrc
配下にapp router
の構成。
ちなみにこういう全体の流れを俯瞰して見れるドキュメント(シーケンス図etc)が欲しかったからやっぱり公式ドキュメントを熟読するのは大切。
TL;DR
[追記]
この記事によると、変数に格納する形でCognitoProvider
も用意しておくと後続処理が少し楽になるらしい
→CognitoProviderに定義した情報を流用可能になるため
機能を実現するために最低限必用なセットアップ
- Cognitoセットアップ
- Google Cloudセットアップ※CognitoでGoogle SSOを行いたい場合
- NextAuthセットアップ
a. NextAuthインストールとRoute Handlerでの初期化 Document
b. CognitoProviderセットアップ
c. 認証状態をアプリケーションで参照するためにsession state(provider)セットアップ Document
d. トークンリフレッシュ処理実装
- next-auathの型を拡張 article
e. オプションセットアップ Document
f. middleware作成
g. コンポーネントでSessionProviderから提供されるセッション情報の利用 Document
1. Cognitoセットアップ
事前準備
※Cognitoからメールを送信する際SESを使用する場合のみ
- Route53でSES用のドメイン登録(※もし使えるドメインが無い場合)
- SESでメアド登録
- SESの制限緩和申請(※商用利用等する場合)
本設定
- Cognitoの設定
- TBD
2. Google Cloudセットアップ
※CognitoでGoogle SSOを利用したい場合
- TBD
3. NextAuthセットアップ
a. NextAuthインストール
ドキュメントに沿ってインストール
※パッケージマネージャは自身が使っているものを使う
bun add next-auth
b. Route Handlerセットアップ
- TBD
- TODO: CognitoProviderのoptionの
checks: nonce
の挙動について調査
動作確認
問題1
諸々設定後サーバー立ち上げてブラウザ操作しようとするとこれが発生...
⨯ node_modules/oidc-token-hash/lib/shake256.js (3:0) @ <unknown>
⨯ Cannot read properties of undefined (reading 'substring')
null
Next.jsとNextAuthのバージョン問題か...?
原因
src/middleware.ts
でnext-auth
からimportしたNextAuth
モジュールをインスタンス化してexportしていたのが原因の様子。(これはv5の書き方なのかな)
import NextAuth from 'next-auth';
import { authConfig } from '@/auth.config';
export default NextAuth(authConfig).auth;
↓にしたらエラーは解消
export {default} from 'next-auth/middleware';
解決の糸口はnextauth middleware
で検索してヒットしたこの記事
src/middleware.ts
でnext-auth
からimportしたNextAuth
モジュールをインスタンス化してexportしていたのが原因の様子。
import NextAuth from 'next-auth';
import { authConfig } from '@/auth.config';
export default NextAuth(authConfig).auth;
↓にしたらエラーは解消
export {default} from 'next-auth/middleware';
解決の糸口はnextauth middleware
で検索してヒットしたこの記事
問題2
問題解消後再度ブラウザでリクエストするとERR_TOO_MANY_REDIRECTS
が発生する。
原因
middleware
のmatcherの指定が悪かった。※根本的な解決ではない気がする
middleware
で認証失敗した場合auth.config.ts
で定義したpages
オプションのsignIn: '/login'
に遷移するが、middleware
のmatcherで/login
も対象に含めてしまっていた為、
/login
にリダイレクト → middlewareで認証処理(未認証) → /login
にリダイレクト → middleware処理...
を繰り返していた。
// NG
export const config = {
matcher: ['/((?!api|_next/static|_next/image|.png).*)'],
};
// OK
export const config = {
matcher: ['/((?!login|api|_next/static|_next/image|.png).*)'],
};
問題3
next-auth
からNextAuthからexportしたsignIn
をactions.ts
で使おうとしたらsignIn
が関数じゃない系のエラーになった。(詳細エラー拾うの忘れた)
// src/auth.ts
import NextAuth from 'next-auth';
import { authConfig } from '@/auth.config';
export const { auth, signIn, signOut } = NextAuth(authConfig);
原因
next-auth/react
からexportしてやる必用があった。
// src/auth.ts
export { signIn, signOut } from 'next-auth/react';
問題4
error: NotAuthorizedException: SecretHash does not match for the client: XXXXXXXXX
SecretHash
が一致しないエラー。
原因
auth.config.ts
のSecretHashを生成している箇所のロジックミス。
環境変数をうまく取得できていなかった。
※COGNITO_CLIENT_SECRET
やCOGNITO_CLIENT_ID
が自分のenvに定義してあるか確認
const secretHash = crypt
.createHmac('sha256', process.env.COGNITO_CLIENT_SECRET ?? '')
.update(email + process.env.COGNITO_CLIENT_ID)
.digest('base64');