【Cognito ユーザープール・Next.js】安全にCognito認証を実装する
はじめに
Cognitoユーザープールを使用したログイン画面の作成方法は、いくつか種類があります。
その中でもAmplify Authを使用した実装をいくつか試してみました。
また、Next.jsを使用し、セキュリティにも配慮した実装も行っていますのでご紹介します。
ライブラリの紹介
amazon-cognito-identity-js
概要
Cognito User Pool 専用の旧フロントエンド向けJavaScriptライブラリ
SRP認証を直接扱える
特徴
クライアント側でSRP認証を行える
コールバックベースAPIが多い
Amplify Authの内部で使われていた
新規開発では非推奨気味(Amplify Auth 推奨)
用途
旧SPAやレガシーコードでSRP認証を直接実装したい場合
Amplify を使いたくないがSRPを使いたい場合
@aws-sdk/client-cognito-identity-provider(SDK v3)
概要
AWS SDK for JavaScript v3 の一部
Cognito User Pool の API を低レベルで呼ぶためのライブラリ
SRPの計算は自分で行う必要あり
特徴
PromiseベースのモダンSDK
サーバーサイドでのユーザー管理(Admin系操作)に最適
サインインフローも呼べるが、SRPやトークン管理は自前で実装する必要がある
モジュール単位で軽量
用途
サーバーサイドでユーザー作成・削除・属性更新
カスタム認証フロー(Lambda連携)
バックエンド管理スクリプトや統合システム
Amplify Auth
概要
フロントエンド(React、Next.js、モバイルなど)向けに高レベルでCognito認証を簡単に使えるライブラリ。
内部的には amazon-cognito-identity-js や AWS SDK を使っている。
特徴
サインイン・サインアップ・MFA・パスワードリセット・SRP認証を自動で処理
TypeScript対応、Promiseベース
フロントエンド開発者が「ほぼ設定だけ」で使える
Hosted UI の利用も可能
SRP認証やトークン管理をライブラリが自動でやってくれる
用途
SPAやモバイルアプリでのユーザー認証
開発速度優先でセキュアにログイン機能を組みたい場合
Amplify Auth セキュリティに関して
トークン保管先について
Amplify Authのデフォルトでは、トークン保管先としてLocal Storageに保存されます。
Next.jsではAmplify.configure(amplifyConfig, { ssr: true })を設定することでCookieに保存することができます。
ただ、Local StorageとCookieのどちらであっても、XSS対策が重要です。
Next.jsではmiddlewareを使用することでHttpOnly Cookieを使用して認証することができます。
「HttpOnly」属性を設定されたCookieには、HTML内のスクリプトからはアクセスできないようになります。
そのため、仮にクロスサイトスクリプティング攻撃を受けたとしても、JavaScriptによるCookie情報の盗み出しを防止することができます。
■middleware・API Routesとは
Middleware は、ユーザーからのリクエストがサーバーに届いてから、Next.js がページや API ルートを処理する直前のタイミングで実行されるものです。
このタイミングでリクエストを検査し、必要に応じて内容を書き換えたり、別のページにリダイレクトしたりといった処理を差し込むことができます。
環境変数について
Next.jsでは、NEXT_PUBLIC_プレフィックスを使用することで、Client側でもServer側でも読み込めるようになります。
amazon-cognito-identity-jsやAmplify Authでのクライアントで認証する場合は、この環境変数が必要になります。
ただ、この環境変数は、ブラウザに送信されるJavaScriptにインライン化されます。
このインライン化はビルド時に行われるため、様々なNEXT_PUBLIC_環境はプロジェクトのビルド時に設定される必要があります。
次のように変数を利用した場合のインライン化されません。
// 変数を使用した指定はインライン化されない
const varName = 'NEXT_PUBLIC_ANALYTICS_ID'
setupAnalyticsService(process.env[varName])
// 変数を使用した指定はインライン化されない
const env = process.env
setupAnalyticsService(env.NEXT_PUBLIC_ANALYTICS_ID)
そもそもNEXT_PUBLIC_プレフィックスをつけずにmiddlewareとAPI Routesを使用してサーバーサイドで環境変数を処理する実装も可能です。
Cognito側の設定
ここからは、Cognitoの設定に入ります。
サンプル実装
サインイン
- ユーザー名
- パスワード
サインアップ
- ユーザー名
- メールアドレス
- パスワード

サインイン識別子のオプション
これは、ユーザーがログイン画面でIDとして入力できるものは何かを決める設定です。
各オプションの意味
- メールアドレス
ユーザーはログイン時にメールアドレス(例: user@example.com)を入力します。
ユーザーID = メールアドレス、という構成になります。 - 電話番号
ユーザーはログイン時に電話番号(例: +819012345678)を入力します。
ユーザーID = 電話番号、という構成になります。 - ユーザー名
ユーザーはサインアップ時に、メールアドレスや電話番号とは別に、自分で好きなユーザー名(例: taro_yamada123)を作成します。
ログイン時には、このユニークなユーザー名を入力します。
サインアップのための必須属性
これは、ユーザーがアカウントを新規作成(サインアップ)するときに、必ず入力しなければならない項目は何かを決める設定です。
ここで選択した属性(例: email, phone_number, given_name(名)など)は、サインアップ画面で必須入力項目になります。
例えば「email」を必須属性にすると、ユーザーはメールアドレスを必ず入力しないとアカウントを作成できません。
認証フロー

| フロー名 | 使い方 | 特徴 |
|---|---|---|
| ALLOW_USER_AUTH(デフォルトオン) | ユーザーが複数の認証方式から選択できる(パスワード、OTP、生体認証、MFAなど) | 複数のサインイン手段を許可し、ユーザーがUIで選ぶ |
| ALLOW_USER_PASSWORD_AUTH | ユーザー名+パスワードを直接送信してサインイン | 実装がシンプルだが、パスワードが直接送られるためTLS必須 |
| ALLOW_USER_SRP_AUTH(デフォルトオン) | SRP(Secure Remote Password)プロトコルでサインイン | パスワードを直接送らずに安全に検証できる。AWS推奨。 |
| ALLOW_ADMIN_USER_PASSWORD_AUTH | サーバー側でユーザー名+パスワードを使ってサインイン | Admin APIでのみ使える。Hosted UIでは不可 |
| ALLOW_CUSTOM_AUTH | Lambdaトリガーでカスタム認証チャレンジを作る | 独自のOTP、外部IdP、秘密質問などを組み込める |
| ALLOW_REFRESH_TOKEN_AUTH(デフォルトオン) | Refresh Tokenを使って新しいトークンを取得 | ユーザー入力なしでセッション更新可能 |
※デフォルトオンとはアプリケーションを定義で シングルページアプリケーション (SPA) を選択したときのデフォルト
SRP(Secure Remote Password)方式とは
- パスワードをネットワーク経由で直接送らない
- クライアントとCognito間で暗号学的なやり取りをして、パスワードが漏れにくい
- AWS公式ではSRPを推奨(ALLOW_USER_SRP_AUTH)
サンプル実装
バージョン
- Next.js:
15.4.6 - React:
19.1.0 - amazon-cognito-identity-js:
6.3.15 - aws-amplify:
6.15.5
環境変数
# AWS Cognito設定
NEXT_PUBLIC_USER_POOL_ID=ユーザープール ID
NEXT_PUBLIC_CLIENT_ID=クライアント ID
# AWS Cognito設定(middleware,API Routes使用時)
USER_POOL_ID=ユーザープール ID
CLIENT_ID=クライアント ID
AWS_REGION=ap-northeast-1
amazon-cognito-identity-js の実装例
npm install amazon-cognito-identity-js
###バージョン###
amazon-cognito-identity-js: 6.3.15
client-ver/
└── src/
└── app/
├── components/
│ ├── AuthWrapper.tsx # 認証チェック用ラッパーコンポーネント
│ ├── LogoutButton.tsx # ログアウトボタンコンポーネント
│ └── UserInfo.tsx # ユーザー情報表示コンポーネント
│
├── login/
│ └── page.tsx # ログインページ
│
├── signup/
│ └── page.tsx # サインアップ(新規登録)ページ
│
├── globals.css # グローバルCSS(Tailwind設定含む)
├── layout.tsx # ルートレイアウト
└── page.tsx # トップページ
//認証フロー図
ユーザー → ログインページ
↓
Cognito認証
↓
トークン取得
↓
localStorage保存
↓
TOPページへ
↓
AuthWrapperで保護
1. 認証ラッパー
// 全ページで認証状態をチェック
useEffect(() => {
const idToken = localStorage.getItem('idToken')
if (!idToken) {
router.push('/login')
}
}, [router])
- クライアントコンポーネントとして実装
- localStorageのトークンをチェック
- 未認証の場合は自動的にログインページへリダイレクト
2. ログインページ
cognitoUser.authenticateUser(authenticationDetails, {
onSuccess: (result: CognitoUserSession) => {
// 3つのトークンを保存
localStorage.setItem('idToken', result.getIdToken().getJwtToken())
localStorage.setItem('accessToken', result.getAccessToken().getJwtToken())
localStorage.setItem('username', username)
router.push('/')
}
})
- IDトークン - ユーザー情報を含むJWT
- アクセストークン - APIアクセス用
- ユーザー名 - 表示用
3. サインアップページ
userPool.signUp(username, password, attributeList, [], callback)
- ユーザー名、メールアドレス、パスワードを登録
- 確認用パスワードとの一致チェック
4. ログアウト機能
const handleLogout = () => {
const cognitoUser = userPool.getCurrentUser()
if (cognitoUser) {
cognitoUser.signOut() // Cognitoからサインアウト
}
// ローカルストレージをクリア
localStorage.removeItem('idToken')
localStorage.removeItem('accessToken')
localStorage.removeItem('username')
router.push('/login')
}
Amplify Auth の実装例
npm install aws-amplify
###バージョン###
aws-amplify: 6.15.5
client-ver-amplifyauth/
└── src/
└── app/
├── components/
│ ├── AmplifyProvider.tsx # Amplify設定プロバイダー
│ ├── AuthWrapper.tsx # 認証チェック用ラッパーコンポーネント
│ ├── LogoutButton.tsx # ログアウトボタンコンポーネント
│ └── UserInfo.tsx # ユーザー情報表示コンポーネント
│
├── lib/
│ └── amplifyConfig.ts # Amplify設定ファイル
│
├── login/
│ └── page.tsx # ログインページ
│
├── signup/
│ └── page.tsx # サインアップ(新規登録)ページ
│
├── globals.css # グローバルCSS(Tailwind設定含む)
├── layout.tsx # ルートレイアウト
└── page.tsx # トップページ
//認証フロー図
ユーザー → Amplify Auth → 自動トークン管理 → セキュアストレージ
↓
自動リフレッシュ
↓
セッション維持
1. Amplify設定
export const amplifyConfig = {
Auth: {
Cognito: {
userPoolId: process.env.NEXT_PUBLIC_USER_POOL_ID!,
userPoolClientId: process.env.NEXT_PUBLIC_CLIENT_ID!,
signUpVerificationMethod: 'code' as const,
loginWith: {
username: true,
email: false
}
}
}
}
2. AmplifyProvider
export default function AmplifyProvider({ children }: { children: React.ReactNode }) {
useEffect(() => {
Amplify.configure(amplifyConfig, { ssr: true })
}, [])
return <>{children}</>
}
- Amplifyの初期化を一元管理
- SSRサポートの有効化
- レイアウトコンポーネントでラップ
3. 認証チェック
// 実際のセッション検証
try {
await getCurrentUser() // Cognitoと通信して検証
} catch {
router.push('/login')
}
- トークンの有効期限を自動チェック
4. ログイン処理
const { isSignedIn } = await signIn({ username, password })
if (isSignedIn) {
localStorage.setItem('username', username) // 表示用のみ保存
router.push('/')
}
- トークン管理が自動化
5. サインアップ処理
// ステップ1: サインアップ
await signUp({
username,
password,
options: {
userAttributes: { email }
}
})
// ステップ2: 確認
await confirmSignUp({
username,
confirmationCode: verificationCode
})
6. ログアウト機能
const handleLogout = async () => {
try {
await signOut()
localStorage.removeItem('username')
router.push('/login')
} catch (error) {
console.error('ログアウトエラー:', error)
}
}
Amplify Auth の実装例(Amplify Auth・middleware・API Routess)
- 環境変数に
NEXT_PUBLIC_を使用しない - リダイレクト処理は
middleware実装 - 認証はクライアント
npm install aws-amplify
###バージョン###
aws-amplify: 6.15.5
client-amplify-auth-apiroute/
└── src/
├── app/
│ ├── api/
│ │ └── config/
│ │ └── route.ts # Amplify設定を返すAPIエンドポイント
│ │
│ ├── components/
│ │ ├── AmplifyProvider.tsx # Amplify設定プロバイダー(API経由で設定取得)
│ │ ├── LogoutButton.tsx # ログアウトボタンコンポーネント
│ │ └── UserInfo.tsx # ユーザー情報表示コンポーネント
│ │
│ ├── login/
│ │ └── page.tsx # ログインページ
│ │
│ ├── signup/
│ │ └── page.tsx # サインアップ(新規登録)ページ
│ │
│ ├── globals.css # グローバルCSS(Tailwind設定含む)
│ ├── layout.tsx # ルートレイアウト
│ └── page.tsx # トップページ(ホームページ)
│
└── middleware.ts # 認証チェック用ミドルウェア
//認証フロー図
ユーザー → Amplify Auth → 自動トークン管理 → Cookie保存
↓
自動リフレッシュ
↓
セッション維持
//認証チェック
ページアクセス → Middleware → Cookie確認 → アクセス許可/拒否
1. 環境変数の保護
// api/config/route.ts
export async function GET() {
const config = {
Auth: {
Cognito: {
userPoolId: process.env.USER_POOL_ID!, // NEXT_PUBLIC_不要
userPoolClientId: process.env.CLIENT_ID!, // サーバーサイドのみ
region: process.env.AWS_REGION!
}
}
}
return NextResponse.json(config)
}
- クライアントバンドルに環境変数が含まれない
- .env.localの内容が露出しない
2. Middleware による認証制御
export function middleware(request: NextRequest) {
// Amplifyが保存するCookieの形式を理解している
const clientId = process.env.CLIENT_ID
const lastAuthUser = request.cookies.get(
`CognitoIdentityServiceProvider.${clientId}.LastAuthUser`
)
const idToken = request.cookies.get(
`CognitoIdentityServiceProvider.${clientId}.${lastAuthUser?.value}.idToken`
)
if (!idToken) {
// 未認証 → ログインページへ
const loginUrl = new URL('/login', request.url)
loginUrl.searchParams.set('from', pathname)
return NextResponse.redirect(loginUrl)
}
}
3. AmplifyProvider の動的設定
// AmplifyProvider.tsx
useEffect(() => {
const configureAmplify = async () => {
// 1. サーバーから設定を取得
const response = await fetch('/api/config')
const config = await response.json()
// 2. Amplifyを設定(SSRモード)
Amplify.configure(config, { ssr: true })
}
}, [])
4. ログインフロー
// login/page.tsx
const handleLogin = async () => {
// 1. Amplify認証
const { isSignedIn } = await signIn({ username, password })
if (isSignedIn) {
// 2. Amplifyが自動的にCookieに保存(ssr: true の効果)
// 3. リダイレクト
router.push(from)
router.refresh() // Middlewareに認証状態を反映
}
}
おわりに
CognitoユーザープールとNext.jsを組み合わせることで、安全かつ柔軟なユーザー認証が可能になります。
今回紹介したSRP認証、Amplify Auth、middlewareを用いたサーバーサイド認証などを理解し、セキュアなSPAやWebアプリケーションの開発に活用してください。
Discussion