AWS Cognito OIDC + NextAuth.jsを使用したSSO(シングルサインオン)認証機能の実装
はじめに
今回は下記のようにどちらもNext.jsのプロジェクトで
https://localhost:3000/ と
https://localhost:3001/ でシングルサインオンを実装しています。
ログアウトすると、https://localhost:3000/ にリダイレクトされるようにしています。
またの機会にNext.jsのプロジェクト以外の場合にSSOを認証機能を追加する方法も記述したいと思っています。
Cognitoとは
AWSが提供する認証、認可、およびユーザー管理サービスを提供するクラウドベースのサービスで、ユーザープールとIDプールという二つの機能で構成されています。
料金
無料枠がありますが、無料枠を超えたものに関してはユーザープールの利用者数と認証イベント、IDプールを使用した認証リクエストの数に基づいて計算されます。
OIDC(OpenID Connect)とは
2001年に導入されたJSONベースのプロトコルでOAuth 2.0をベースとしているインターネット上で安全にユーザー認証を行うための規格でユーザー認証に特化しています。
誰がこの人かを確認するための技術であり、ログインする際によく見る「Googleでログイン」などの機能に使われています。
OIDCは、認証時にIDトークンを使用します。このIDトークンは、ユーザーの身元情報を含むJWT(JSON Web Token)で、認証サービスによって発行され、安全に情報をエンコードし、デジタル署名されます。
JWT(JSON Web Token)とは
ウェブトークンの1つの形式です。
JWTはヘッダー、ペイロード、シグネチャの三つの部分から成り立っています。それぞれがピリオド(.
)で区切られています。
シングルサインオンとは
ユーザーが一度の認証で複数の異なるシステムやアプリケーションにアクセスできる技術です。これにより、ユーザーは多くの異なるサービスを使う際に、それぞれでログイン情報を入力する必要がなくなります。
シングルサインオンの仕組み
ユーザーが初めてログインする際、認証システムにユーザー名とパスワードを入力します。
→認証が成功すると、認証システムはユーザーのブラウザに一定期間有効な認証トークンを発行します。
→ユーザーが他のシステムにアクセスする際、そのシステムは認証システムにトークンの確認を求めます。
→トークンが有効であれば、追加のログインなしでそのシステムにアクセスできます。
OIDCはこの仕組みの中で、認証トークンの発行、確認を行っています。
インターネット上での認証や認可を扱うプロトコルには、OIDCの他に
SAML(Security Assertion Markup Language)、OAuthがありますが役割や用途において異なります。
SAML
2001年に導入されたXMLベースのプロトコルで、XMLを扱うための特別なパーサーやライブラリが必要です。
SAMLはセキュリティアサーション(主張)をXML形式で交換し、これによりユーザーが認証され、アプリケーション間でのユーザーのアイデンティティが確認されます。
OAuth
インターネット上でユーザーが自分のアカウント情報を安全に第三者アプリケーションに共有することを許可するためのプロトコルです。
例えば、ユーザーが新しいアプリに登録するときに「Googleでログイン」を選択する場合にOAuthが使用されてと、そのアプリはユーザーの基本情報にアクセスするための認可をGoogleから受け取っています。
ユーザープールとは
ユーザーの情報(名前やメールアドレス)などを保存し、ユーザー管理する機能で、認証プロバイダ(ユーザーのアイデンティティを検証するサービスやシステム)の役割も果たします。
ユーザープールを作成する
ユーザープールを作成
を選択、
フェデレーテッドアイデンティティプロバイダー
を選択、
Cognito ユーザープールのサインインオプション
を選択(今回はEmailを選択しました)、
フェデレーティッドサインインのオプション
> OpenID Connect (OIDC)
を選択して、次へ
を押してください。
今回は多要素認証
> MFA なし
を選択して、次へ
を押してください。
サインアップエクスペリエンスを設定
では特に変更しないので、そのまま次へ
を押してください。
メッセージ配信を設定
> Eメール
では
Cognito で E メールを送信
を選択して、次へ
を押してください。
Amazon SES で E メールを送信 - 推奨
を選択した場合は、送信元の E メールアドレス
(SESでEメールアドレスの検証が完了しているメールアドレス)を選択して、次へ
を押してください。
フェデレーテッドアイデンティティプロバイダーを接続では後で
を選択し、次へ
を押してください。
アプリケーションを統合 > ユーザープール名
を入力、
クライアントのシークレットを生成する
を選択してください。
Cognito ドメイン
を入力、
最初のアプリケーションクライアント > アプリケーションクライアント名
を入力
許可されているコールバック URL
にはhttps://<ドメイン名>/api/auth/callback/cognito
を入力したら、下へスクロースして
高度なアプリケーションクライアントの設定
> サインアウト URL を追加
を選択して、
https://<ドメイン名>/signout
を入力したら次へ
を押してください。
内容を確認して、ユーザープールを作成
を押してください。ユーザープールが作成されます。
IDプールとは
ユーザーが外部の認証プロバイダーや自身のCognitoユーザープールを通じて認証された後、AWSリソースに対するアクセス許可を付与するために使用される機能です。
IDプールを作成する
ID プールを作成
を押してください。
ユーザーアクセス
> 認証されたアクセス
を選択、
認証された ID ソース
> 認証プロバイダとしてAmazon Cognito ユーザープール
を選択して、次へ
を押してください。
IAM ロール
> 新しい IAM ロールを作成
を選択し、IAM ロール名を入力して、次へ
を押してください。
ユーザープール ID
とアプリクライアント ID
を選択して、次へ
を押してください。(アプリクライアント IDはユーザープール IDを選択すると自動で表示されます。)
ID プール名を入力して、次へ
を押してください。
内容を確認して、ID プールを作成
を押してください。
認証プロバイダーの統合
サインインエクスペリエンス
を選択、フェデレーテッドアイデンティティプロバイダーのサインイン
> アイデンティティプロバイダーを追加
を選択してください。
フェデレーテッドアイデンティティプロバイダー
異なるシステムやアプリケーション間でユーザーの認証情報を共有するために使用されるサービスのことです。
OpenID Connect (OIDC)
を選択して、
プロバイダー名、クライアントID、クライアントシークレットを入力してください。
クライアントIDはユーザープール > アプリケーションの統合
を選択、下にスクロールするとあります。
この部分にあります。
発行者URLには、https://cognito-idp.<リージョン名>.amazonaws.com/<ユーザープールID>
OpenID Connect属性は、email
と入力して、アイデンティティプロバイダーを追加
を押してください。
ちなみに東京リージョンは、ap-northeast-1
です。
以降の記述を各プロジェクトに記述することでSSOを実現しています。
NextAuth.jsライブラリのインストール
npm install next-auth
NextAuth.jsとは
NextAuth.jsは、Next.jsアプリケーション向けの認証ライブラリです。
このライブラリは、開発者がNext.jsプロジェクトに簡単に認証機能を組み込むことができるように設計されています。OAuth、Email、さらには自分で定義した認証プロバイダを使用してユーザーを認証する機能を提供しています。
セッション
NextAuth.jsのデフォルト設定では、セッション情報はクッキーに保存されます。
認証APIの設定
Next.jsのAPIルートを使用した認証APIを設定するためのファイルを作成します。
シングルクォート ('
) を使ってファイル名全体を囲むことで、角括弧を特別な意味を持たないただの文字として扱うようにしています。
mkdir -p 'src/app/api/auth/[...nextauth]' && touch 'src/app/api/auth/[...nextauth]/route.ts'
Next.jsプロジェクトでNextAuth.jsを使ってCognitoを認証プロバイダとして設定してください。具体的には、ユーザーがAWS Cognitoを通じて認証できるようにするための設定をしています。
import NextAuth from 'next-auth';
import CognitoProvider from 'next-auth/providers/cognito';
const handler = NextAuth({
providers: [
CognitoProvider({
clientId: process.env.COGNITO_CLIENT_ID || '',
clientSecret: process.env.COGNITO_CLIENT_SECRET || '',
issuer: process.env.COGNITO_DOMAIN,
}),
],
});
export { handler as GET, handler as POST };
export { handler as GET, handler as POST }
サーバーが受け取るGETリクエストとPOSTリクエストに対して、handler
関数を使って応答を処理するように設定しています。
認証セッション管理の設定
アプリケーション全体に認証セッション情報を提供するためのコンポーネントを定義します。
mkdir -p src/app/providers && touch src/app/providers/NextAuth.tsx
'use client';
import { SessionProvider } from 'next-auth/react';
import { ReactNode } from 'react';
const NextAuthProvider = ({ children }: { children: ReactNode }) => {
return <SessionProvider>{children}</SessionProvider>;
};
export default NextAuthProvider;
import type { Metadata } from 'next';
import { Inter } from 'next/font/google';
import './globals.css';
import NextAuthProvider from './providers/NextAuth';
const inter = Inter({ subsets: ['latin'] });
export const metadata: Metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
};
export default function RootLayout({
children,
}: Readonly<{
children: React.ReactNode;
}>) {
return (
<html lang='ja'>
<NextAuthProvider>
<body className={inter.className}>{children}</body>
</NextAuthProvider>
</html>
);
}
ログイン状況の表示
'use client';
import { signIn, signOut, useSession } from 'next-auth/react';
const { data: session } = useSession();
console.log('session', session);
if (session && session.user) {
return (
<main>
<p>ログイン中</p>
<br />
{session.user.email}でログインしています。
<br />
<button onClick={() => signOut()}>Sign out</button>
</main>
);
}
return (
<main>
ログインしていません。
<br />
<button onClick={() => signIn()}>Sign in</button>
<br />
<button onClick={() => signIn('cognito')}>Cognito直通Sign in</button>
</main>
);
Sign inの場合
Cognito直通Sign inの場合
環境変数の設定
COGNITO_CLIENT_ID=
COGNITO_CLIENT_SECRET=
COGNITO_DOMAIN=https://cognito-idp.<リージョン名>.amazonaws.com/<ユーザープール ID>
NEXTAUTH_URL=https://<ドメイン名>
NEXTAUTH_SECRET=何か適当な文字列
Try signing in with a different account.
私の場合、間違った認証情報(COGNITOのドメイン)が誤っておりこちらのエラーが表示されていました。
ユーザー情報を表示したい場合
認証コードを元にアクセストークンを取得
→アクセストークンを元にユーザー情報を取得してください。
NextAuth.js を使って Amazon Cognito をカスタムプロバイダーとして設定
UIのカスタマイズ
カスタムログイン
[...nextauth].ts
型拡張
クライアントシークレットを生成している場合
Cognitoでの認証リクエストを送信する際にシークレットハッシュが必須になります。
SecretHashを生成
NextAuth Beta版v5
ENOENT: no such file or directory, open '/Users/ユーザー名/node_modules/events/events.js'
rm -rf node_modules
rm package-lock.json
npm install
実行後に再度開発環境を起動すると解決しました。
npm run dev
終わりに
何かありましたらお気軽にコメント等いただけると助かります。
ここまでお読みいただきありがとうございます🎉
Discussion