Next.js+TSでNextAuth.jsを使ってcognito認証を行う

24568 ワード

webアプリを作っていて、ログイン部分の実装のためにcognito認証を使ったので、その方法について書きます。
今回はAWS側で予め用意されているhosted UIを使う場合の記事となります。

Cognito側の設定

Amazon Cognito コンソールに移動して、ログイン後ユーザープールを作成します。
アプリクライアントの設定が必要になるので、設定しておきます。

ユーザープールが作成できたらアプリの統合 > アプリクライアントの設定からログイン時のコールバックURLに http://localhost:3000/api/auth/callback/cognito と入力します。

あとは必要なOAuthスコープを選択して、適当なドメイン名を追加すれば設定完了です。

NextAuth.jsのインストール

まずNextAuth.js[1]をインストールします。

yarn add next-auth

必要な環境変数の確認

https://next-auth.js.org/providers/cognito を読んで必要なenvの値をセット&&型補完がきくようにglobals.d.tsを追加します。(この手順は好みなのでなくても良いです)

globals.d.ts
declare namespace NodeJS {
  // 環境変数名の定義
  interface ProcessEnv {
    readonly COGNITO_CLIENT_ID: string;
    readonly COGNITO_CLIENT_SECRET: string;
    readonly COGNITO_ISSUER: string;
  }
}

COGNITO_CLIENT_ID、COGNITO_CLIENT_SECRET、COGNITO_ISSUERに関してはそれぞれawsコンソールから確認して、.env.localに追加しておきます。

APIルートを追加

上記でコールバックURLに指定したhttp://localhost:3000/api/auth/callback/cognito に当たるルートの処理を書きます。

apiフォルダ配下にauthを作成し、[...nextauth].tsという名前のファイルを作成します。

pages/api/auth/[...nextauth].ts
import NextAuth from 'next-auth';
import CognitoProvider from 'next-auth/providers/cognito';

export default NextAuth({
  providers: [
    CognitoProvider({
      clientId: process.env.COGNITO_CLIENT_ID,
      clientSecret: process.env.COGNITO_CLIENT_SECRET,
      issuer: process.env.COGNITO_ISSUER,
    }),
  ],
});

これでhttp://localhost:3000/api/auth/callback/cognito にアクセスされた場合、上記の処理が走り、cognito認証ができるようになります。

クライアント側からcognitoログインできるようにする

最後に、クライアント側からcognito認証するための動線を追加します。
まずはSessionProviderでコンポーネントをラップします。

_app.tsx
import { SessionProvider } from 'next-auth/react';
import type { AppProps } from 'next/app';
import '../styles/globals.css';

function MyApp({ Component, pageProps: { session, ...pageProps } }: AppProps) {
  return (
    <SessionProvider session={session}>
      <Component {...pageProps} />
    </SessionProvider>
  );
}

export default MyApp;

これでNextAuth.js側で用意されているhooks類などが呼び出せるようになります。
接続できることを確認するために、https://next-auth.js.org/getting-started/example#frontend---add-react-hook にかかれてあるとおりの処理をindex.tsxに追加します。

index.tsx
import type { NextPage } from 'next';
import { signIn, signOut, useSession } from 'next-auth/react';

const Home: NextPage = () => {
  const { data: session } = useSession();
  if (session && session.user) {
    return (
      <>
        Signed in as {session.user.email} <br />
        <button onClick={() => signOut()}>Sign out</button>
      </>
    );
  }
  return (
    <>
      Not signed in <br />
      <button onClick={() => signIn()}>Sign in</button>
    </>
  );
};

export default Home;

起動するとSigninというボタンが見えます。

クリックすると以下の画面になります。※この画面はカスタム可能です(後述)

これを更にクリックするとhosted UIが現れます。

カスタマイズ

[...nextauth].ts内にpagesを追加し、サインインページなどを別途指定することもできます。

[...nextauth].ts
import NextAuth from 'next-auth';
import CognitoProvider from 'next-auth/providers/cognito';

export default NextAuth({
  providers: [
    CognitoProvider({
      clientId: process.env.COGNITO_CLIENT_ID,
      clientSecret: process.env.COGNITO_CLIENT_SECRET,
      issuer: process.env.COGNITO_ISSUER,
    }),
  ],
+  pages: {
+    signIn: '/auth/signin',
+    signOut: '/auth/signout',
+    error: '/auth/error', // Error code passed in query string as ?error=
+    verifyRequest: '/auth/verify-request', // (used for check email message)
+    newUser: '/auth/new-user' // New users will be directed here on first sign in (leave the property out if not of interest)
+  }
});