Next.js 13でAWS Cognitoを使用したGoogle認証を試す(NextAuth.jsを使って。)
やりたいこと
AWSのCognitoを使用してGoogleアカウントの認証を行いたい。使うフレームワークはNext.js 13とする。
環境
- Windows 11
- Next.js version 13.4.19
- NextAuth.js version 4.23.1
- TypeScript version 5.2.2
- TailWindCSS
Google APIの設定から
Google Cloud でプロジェクトを作成する。作成したプロジェクトを選び、認証情報タブでOAuth2.0クライアントを作成する。
その後下記の表通りに設定する。
入力欄 | 入力内容 |
---|---|
アプリケーションの種類 | ウェブアプリケーション |
名前 | 自分の好きな名前 |
承認済みのJavascript生成元 | http://localhost:12000 |
承認済みのリダイレクトURL | 空欄 |
これらを入力したら作成する。
AWS Cognitoの設定
ユーザプールの作成
ユーザプールの作成ボタンを押す。
認証プロバイダーの設定
下記の表のとおりにする。
入力欄 | 入力内容 |
---|---|
プロバイダーのタイプ | フェデレーテッドアイデンティティプロパイダ― |
Cognito ユーザープールのサインインオプション | Eメール |
フェデレーティッドサインインのオプション |
パスワードポリシー
入力欄 | 入力内容 |
---|---|
パスワードポリシーモード | カスタムでもデフォルトでもいい |
多要素認証とユーザーアカウントの復旧
好みの設定でいい。
サインアップエクスペリエンスを設定
セルフサービスのサインアップ
ここでサインアップできるようにするか選択する。
いろいろ好きに設定する。
フェデレーテッドアイデンティティプロバイダーを接続
この設定でGoogleプロバイダーと連携する。
入力欄 | 入力内容 |
---|---|
クライアントID | Google APIの設定で確認したクライアントID |
クライアントシークレット | Google APIで確認したシークレットキ― |
許可されたスコープ | emailとか |
Googleとユーザープール間でマッピング | emailとemail |
「アプリケーションを統合」の設定
入力欄 | 入力内容 |
---|---|
ユーザープール名 | なんでもいい |
Cognitoドメイン | 使えるやつなら何でもいい |
最初のアプリケーションクライアント | 秘密クライアント |
アプリケーションクライアント名 | なんでもいい |
クライアントシークレット | クライアントのシークレットを作成する |
許可されているコールバックURL | http://localhost:12000/api/auth/callback/cognito |
この設定でユーザープールを作成する。
アプリケーションの統合の確認
作成したユーザープールを選択し、「アプリケーションの統合」タブを選択する。そして一番下の「アプリケーションクライアントのリストのアプリクライアントと分析」の最初のアプリケーションクライアントで入力した名前をクリックする。
クライアントIDとクライアントシークレットを確認する。また、ホストされたUIで編集ボタンを押して、IDプロバイダーにCognitoだけでなく、Googleも追加する。(自分はこの部分を行っていなかったためログインページにGoogleログインボタンが出なかった。)
Google APIの追加設定
OAuthクライアントIDの認証情報の追加設定
承認済みのリダイレクトURLに「アプリケーションを統合」の設定で決めたCognitoドメインを入力する。
例:https://XXXXXX.auth.ap-northeast-1.amazoncognito.com/oauth2/idpresponse
Next.js 13の設定 (App Router使用)
Next-Authの設定
next-authのインストール
npm install next-auth
route.tsxの作成
src/app/api/auth/[...nextauth]/route.tsxを作成し、下記のコードを入力する・
import NextAuth from 'next-auth'
import CognitoProvider from 'next-auth/providers/cognito'
const handler = NextAuth({
secret: process.env.NEXTAUTH_SECRET,
providers: [
CognitoProvider({
//本当は型定義したほうがいいけどmんどくさいので''を使う。
clientId: process.env.COGNITO_CLIENT_ID ?? '',
clientSecret: process.env.COGNITO_CLIENT_SECRET ?? '',
issuer: process.env.COGNITO_ISSUER ?? '',
checks: 'nonce',
//checks: 'nonce'がないとサインインできるけどエラーが起きて別のアカウントで試せと言われる
}),
],
})
export { handler as GET, handler as POST }
プロジェクトのルートディレクトリに.envファイルを作成する
COGNITO_CLIENT_ID = CognitoのクライアントID
COGNITO_CLIENT_SECRET = Cognitoのシークレットキー
COGNITO_ISSUER = https://cognito-idp.{cognitoのregion}.amazonaws.com/{ユーザープールID}
NEXTAUTH_URL=http://localhost:12000
//サインイン後のリダイレクトURL
NEXTAUTH_SECRET="ランダムな文字列"
NextAuth.tsxを作成
/src/providers/NextAuth.tsxを作成する。
'use client'
import { SessionProvider } from 'next-auth/react'
import React from 'react'
import { ReactNode } from 'react'
const NextAuthProvider = ({ children }: { children: ReactNode }) => {
return <SessionProvider>{children}</SessionProvider>
}
export default NextAuthProvider
このファイルでSessionProviderをwrapしている。
layout.tsxの設定
NextAuthコンポーネントを/src/app/layout.tsxで読み込んでアプリのどこからでもログイン状態を確認できるようにする。
import './globals.css'
import { Inter } from 'next/font/google'
import NextAuthProvider from '@/providers/NextAuth'
const inter = Inter({ subsets: ['latin'] })
export const metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}
export default function RootLayout({ children }: { children: React.ReactNode }) {
return (
<html lang='ja'>
<body className={inter.className}>
<NextAuthProvider>{children}</NextAuthProvider>
</body>
</html>
)
}
ログインボタンなどのコンポーネントの作成
"use client";
import {signIn, signOut} from "next-auth/react";
import React from "react";
// ログインボタン
export const LoginButton = () => {
return (
<button onClick={() => signIn()} className="mt-4 bg-blue-500 hover:bg-blue-300 active:scale-90 transition-all duration-200 text-white py-2 px-4 rounded-full mx-auto">
サインイン
</button>
);
};
// ログアウトボタン
export const LogoutButton = () => {
return (
<button className="mt-4 bg-red-500 hover:bg-red-300 active:scale-90 transition-all duration-200 text-white py-2 px-4 rounded-full mx-auto" onClick={() => signOut()}>
サインアウト
</button>
);
};
'use client'
import { useSession } from 'next-auth/react'
import React from 'react'
export default function isLoggedIn() {
const { data: session } = useSession()
if (!session) {
return <p>ログインしていません。</p>
}
return (
<div>
{session.user ? `${session.user.email}としてログインしています。` : '読み込み中...'}
</div>
)
}
page.tsxの設定
'use client';
import React, { use } from 'react'
import { LoginButton, LogoutButton } from '@/components/AuthenticateButton'
import IsLoggedin from '@/components/IsLoggedin'
export default function Home() {
return (
<main className='flex h-screen justify-center items-center'>
<div className='text-center'>
<h1 className='text-4xl font-bold'>Next Auth</h1>
<IsLoggedin/>
<LoginButton/>
<LogoutButton/>
</div>
</main>
)
}
テストしてみる
URL http://localhost:12000 にアクセスする。
サインインできる!!
参考
つまづいたところ
- CognitoのクライアントIDなのかGoogleAPIのクライアントIDなのかが曖昧な部分があり、エラーが多発した。
- アプリケーションクライアントの統合を忘れてCognitoのボタンしか出ず、解決に時間を要した。
- Cognito側の問題なのだがサードパーティ製の認証を行おうとすると、nonce:dismatchというエラーが出てしまい、ログインはできるのだがリダイレクト先がエラーページになってしまい、その解決に時間がかかった。結局Next.jsのCognitoProviderに
checks:'nonce'
を設定し解決した。
Discussion