【Next.js】App Router × NextAuth.jsでGoogleログイン機能を実装したい

## 概要

おつかれさまです！
個人開発でログイン機能を実装するにあたり、NextAuth.jsを採用することにしたので今回はその方法についてまとめてみました！

## NextAuth.jsとは
https://next-auth.js.org/

OAuthの仕組みをもとにNext.js向けに作られたライブラリです。
GoogleやGitHubなど、OAuthを使った認証サービスが簡単に利用できるように作られています。

::: note
OAuthについてはこの記事が大変わかりやすかったです!
https://qiita.com/TakahikoKawasaki/items/e37caf50776e00e733be
:::


## 手順
手順としては以下のようになります！

1. GoogleAPIの設定
2. .envの設定
3. Dynamic Routesの設定
3. Session Providerの設定
3. コンポーネントの作成
3. 実装の確認 


## 1. GoogleAPIの設定
下記記事を参考にAPIの設定を行います。
最終的に``ClientId``と``ClientSecret``が発行されるので、コピーして保存しておきましょう。

https://shirotamaki.hatenablog.com/entry/2023/03/26/155103

## 2. .envの設定
発行したAPIの情報を.envファイルに保存し参照できるようにしておきます。
この際、認証にアプリケーションのURLも必要になってくるため、``NEXTAUTH_URL``で設定をしておきます。

また、認証の際に``NEXTAUTH_SECRET``も必要になってくるため下記のコマンドで値を生成しておきます。

```javascript:command
openssl rand -base64 32
```

```ruby:.env
GOOGLE_CLIENT_ID="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
GOOGLE_CLIENT_SECRET="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

NEXTAUTH_URL="http://localhost:3000"
NEXTAUTH_SECRET="xxxxxxxxxxxxxxxxxx"
```

## 3. Dynamic Routesの設定
まず、``NextAuth``をnpmでインストールしておきます。

```javascript
npm install next-auth
```

次にApp RouterでのDynamicRoutesを設定します。追加するルーティングは``src/app/api/auth/[...nextauth]/route.ts``です。
``[...nextauth]``は、``Dynamic Segments``と呼ばれ、ルート階層が深くなってもリクエストできるような仕組みを作ることができます。

```javascript:src/app/api/auth/[...nextauth]/route.ts
import NextAuth from 'next-auth';
import GoogleProvider from 'next-auth/providers/google';

const handler = NextAuth({
	secret: process.env.NEXTAUTH_SECRET,
	providers: [
		GoogleProvider({
			clientId: process.env.GOOGLE_CLIENT_ID ?? '',
			clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? '',
		}),
	]
});
export { handler as GET, handler as POST };
```


## 4. Session Providerの設定
ログイン後に、session情報を各ページで使いたい為、設定をしておきます。
```javascript:src/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;
```
::: note warn
SessionProviderはuseContextを含むので`use client`を付ける必要があります。
:::

後はグローバルに参照できるように`layout.tsx`で読み込んでおきます。
```javascript: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: Metadata = {
	title: 'xxxxxxx',
	description: 'xxxxxxx',
};

export default function RootLayout({
	children,
}: {
	children: React.ReactNode;
}) {
	return (
		<html lang="en">
			<body className={inter.className}>
				<NextAuthProvider>{children}</NextAuthProvider>
			</body>
		</html>
	);
}
```

## 5. コンポーネントの作成
処理の実装ができたので、コンポーネントを作って実際に動作を確認していきます。
```javascript:app/components/Login.tsx
import React from 'react';
import { useSession, signIn } from 'next-auth/react';

export default function Login() {
	const { data: session, status } = useSession();

	if (status === 'loading') {
		return <div>Loading...</div>;
	}

	if (status !== 'authenticated') {
		return (
			<div>
				<p>あなたはログインしていません</p>
				<button onClick={() => signIn('google', {}, { prompt: 'login' })}>
					Googleでログイン
				</button>
			</div>
		);
	}
	return null;
}
```

```javascript:app/components/Logout.tsx
import React from 'react';
import { useSession, signOut } from 'next-auth/react';

export default function Logout() {
	const { data: session, status } = useSession();

	if (status === 'authenticated') {
		return (
			<div>
				<button onClick={() => signOut()}>ログアウト</button>
			</div>
		);
	}
	return null;
}

```
``useSession()``と``signIn()``、``signOut()``はNextAuthが提供しているフックです。
``useSession()``を使うことでsessionの状態を参照でき、``signIn()``でログイン処理を実行させています。

```javascript
'use client';
import { useSession } from 'next-auth/react';
import Login from './components/Login';
import Logout from './components/Logout';

export default function Home() {
	const { data: session, status } = useSession();
	return (
		<div>
			{status === 'authenticated' ? (
				<div>
					<p>セッションの期限：{session.expires}</p>
					<p>ようこそ、{session.user?.name}さん</p>
					<img
						src={session.user?.image ?? ``}
						alt=""
						style={{ borderRadius: '50px' }}
					/>
					<div>
						<Logout />
					</div>
				</div>
			) : (
				<Login />
			)}
		</div>
	);
}
```
## 6.実装の確認 
画面表示はこのようになります。
[![Image from Gyazo](https://i.gyazo.com/79f76026c8a0be00a4be6b98fd13a222.png)](https://gyazo.com/79f76026c8a0be00a4be6b98fd13a222)

``Googleでログイン``をクリックすると、Googleログイン画面へ遷移し、アカウントを選択すると、画像のようにログイン処理とアカウント情報を取得することができました。

また、`ログアウト`を押しても正常にログアウトすることができます。
[![Image from Gyazo](https://i.gyazo.com/aef1dce8c9a01dc130b970f6b5238a5f.png)](https://gyazo.com/aef1dce8c9a01dc130b970f6b5238a5f)

## まとめ
認証周りの知識がまだまだですが、改めて学ぶきっかけになりました！
最後まで読んで頂き、ありがとうございました！