在 Next 13.0 使用 NextAuth.js 認證套件

做法

建立專案。

1
2
npx create-next-app@latest
cd next-auth-example

安裝依賴套件。

1
npm install next-auth

實作

建立 Provider

新增 components/Provider.js 檔。

1
2
3
4
5
6
7
8
9
10
11
'use client';

import { SessionProvider } from 'next-auth/react';

export default function Provider({ children }) {
return (
<SessionProvider>
{children}
</SessionProvider>
);
}

修改 layout.js 檔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import './globals.css'
import { Inter } from 'next/font/google'
import Provider from '@/components/Provider'

const inter = Inter({ subsets: ['latin'] })

export const metadata = {
title: 'Create Next App',
description: 'Generated by create next app',
}

export default function RootLayout({ children }) {
return (
<html lang="en">
<body className={inter.className}>
<Provider>
{children}
</Provider>
</body>
</html>
)
}

建立 API

新增 app/api/auth/[...nextauth]/route.js 檔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';

const handler = NextAuth({
pages: {
signIn: '/sign-in',
},
providers: [
CredentialsProvider({
async authorize({ email, password }) {
if (email === 'test@example.com' && password === 'password') {
return { token: 'token', email };
}
throw new Error('Invalid credentials');
},
}),
],
callbacks: {
async jwt({ token, user }) {
if (user) {
token.accessToken = user.token;
}
return token;
},
async session({ session, token }) {
session.accessToken = token.accessToken;
return session;
},
},
});

export { handler as GET, handler as POST };

建立 UI

修改 app/page.js 檔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
'use client';

import { useRouter } from 'next/navigation';
import { useEffect } from 'react'

export default function Home() {
const router = useRouter();
useEffect(() => {
router.push('/sign-in');
}, []);
return (
<div />
);
}

新增 app/dashboard/page.js 檔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
'use client';

import { signOut, useSession } from 'next-auth/react';
import { redirect } from 'next/navigation';

export default function Dashboard() {
const { data: session } = useSession();

console.log('session', session);

return (
<>
<button
type="button"
onClick={() => signOut()}
>
Sign Out
</button>
</>
);
}

新增 app/sign-in/page.js 檔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
'use client';

import { signIn } from 'next-auth/react';
import { useRouter } from 'next/navigation';
import { useState } from 'react';

export default function SignIn() {
const router = useRouter();

const [email, setEmail] = useState('test@example.com');
const [password, setPassword] = useState('password');

const submit = async (e) => {
e.preventDefault();
const result = await signIn('credentials', {
email,
password,
redirect: false,
});
console.log(result);
if (result.error) {
alert(result.error);
return;
}
router.push('/dashboard');
};

return (
<>
<form onSubmit={submit}>
<input
type='text'
placeholder="Email"
defaultValue={email}
onChange={(e) => setEmail(e.target.value)}
/>
<input
type="password"
placeholder="Password"
defaultValue={password}
onChange={(e) => setPassword(e.target.value)}
/>
<button
type="button"
onClick={submit}
>
Sign In
</button>
</form>
</>
);
}

新增 app/sign-out/page.js 檔。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
'use client';

import { signOut, useSession } from 'next-auth/react';
import { useEffect } from 'react';
import { useRouter } from 'next/navigation';

export default function SignIn() {
const router = useRouter();
const { data: session } = useSession();

useEffect(() => {
(async () => {
if (session) {
await signOut({
redirect: false,
});
}
router.push('/sign-in');
})();
}, [router, session]);

return (
<div />
);
}

建立 Middleware

新增 .env 檔。

1
NEXTAUTH_SECRET=test

新增 middleware.js 檔。

1
2
3
4
5
6
7
export { default } from 'next-auth/middleware';

export const config = {
matcher: [
'/((?!api|sign-up|sign-in|forgot-password|sign-out|_next|.*\\..*|$).*)',
],
};

啟動

啟動。

1
npm run dev

前往 http://localhost:8080 瀏覽。

程式碼

參考資料