/

nextjs-email-authentication

#使用NextAuth的Next.js電子郵件身份驗證

在Next.js中管理身份驗證可以有很多不同的方法。

在我的網站上,我選擇使用基於電子郵件的身份驗證和JWT令牌,通過NextAuth.js來實現,下面是我是如何做到的。

首先,你需要一個外部數據庫。你可以使用本地數據庫或者雲端數據庫。我選擇了PostgreSQL,但你可以使用任何你想要的數據庫。

假設你已經建立了一個Next.js網站。

執行npm install next-auth pg安裝NextAuth和PostgreSQL庫。

然後在你的.env文件中添加以下內容:

1
2
3
4
5
DATABASE_URL=<輸入postgresql://數據庫的URL>
EMAIL_SERVER=smtp://user:[[email protected]](/cdn-cgi/l/email-protection):465
EMAIL_FROM=你的名字 <[[email protected]](/cdn-cgi/l/email-protection)>
NEXTAUTH_URL=http://localhost:3000
SECRET=密鑰

確保你添加了一個密鑰。你可以使用https://generate-secret.vercel.app/32生成。

我使用https://mailtrap.io來測試郵件,這在你設置好一切時非常方便。

創建一個具有以下內容的pages/api/auth/[...nextauth].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
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'

export default NextAuth({
providers: [
Providers.Email({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM,
}),
],

database: process.env.DATABASE_URL,
secret: process.env.SECRET,

session: {
jwt: true,
maxAge: 30 * 24 * 60 * 60, // 30 days
},

jwt: {
secret: 'INp8IvdIyeMcoGAgFGoA61DdBglwwSqnXJZkgz8PSnX', //在這裡使用一個隨機密鑰令牌
encryption: true,
},

debug: true,
})

現在根據你的數據存取層進行配置。如果你使用Prisma ORM,可以使用以下命令安裝@next-auth/prisma-adapter

1
npm install @next-auth/prisma-adapter

然後在[...nextauth].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
import NextAuth from 'next-auth'
import Providers from 'next-auth/providers'
import { PrismaAdapter } from '@next-auth/prisma-adapter'
import prisma from 'lib/prisma'

export default NextAuth({
providers: [
Providers.Email({
server: process.env.EMAIL_SERVER,
from: process.env.EMAIL_FROM
})
],

database: process.env.DATABASE_URL,
secret: process.env.SECRET,

session: {
jwt: true,
maxAge: 30 * 24 * 60 * 60 // 30 days
},

jwt: {
secret: 'INp8IvdIyeMcoGAgFGoA61DdBglwwSqnXJZkgz8PSnX', //在這裡使用一個隨機密鑰令牌
encryption: true
},

debug: true,
adapter: PrismaAdapter(prisma)
})

你需要在schema.prisma中添加4個模型:

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
model VerificationRequest {
id String @id @default(cuid())
identifier String
token String @unique
expires DateTime
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt

@@unique([identifier, token])
}

model Account {
id String @id @default(cuid())
providerType String
providerId String
providerAccountId String
refreshToken String?
accessToken String?
accessTokenExpires DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
userId Int
@@unique([providerId, providerAccountId])
}

model Session {
id String @id @default(cuid())
expires DateTime
sessionToken String @unique
accessToken String @unique
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
user User @relation(fields: [userId], references: [id])
userId Int
}

model User {
id String @id @default(cuid())
name String?
email String? @unique
emailVerified DateTime?
image String?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
accounts Account[]
sessions Session[]
}

記得在修改模型後運行npx prisma migrate dev以應用更改到數據庫。

現在打開pages/_app.js並添加以下代碼:

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

在你的<Component />調用之前使用它進行包裹:

1
2
3
4
5
return (
<SessionProvider session={pageProps.session}>
<Component {...pageProps} />
</SessionProvider>
)

現在在你的應用程序中添加一個鏈接,指向/api/auth/signin。這將是登錄表單。

最後,在需要需要已登錄的會話的頁面中,首先引入useSession鈎子:

1
import { useSession } from 'next-auth/react'

然後使用它來獲取狀態信息。當會話信息仍在加載時,loading為true。

1
const { data: session, status } = useSession()

我們可以使用這個session對象在用戶登錄時在屏幕上顯示信息:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
{session && (
<p>
{session.user.email}{' '}
<button
className="underline"
onClick={() => {
signOut()
router.push('/')
}}
>
logout
</button>
</p>
)}

我們還可以使用這個信息,除非我們已經完成加載,並且會話已建立:

1
2
3
4
5
6
7
8
9
if (typeof window !== 'undefined' && loading) return null

if (typeof window !== 'undefined' && !session) {
router.push('/api/auth/signin')
}

if (!session) { //用於服務器端渲染
return null
}

如果用戶未登錄,我會將瀏覽器重定向到/api/auth/signin。你也可以創建一個自定義表單,但這是基本的方法。

在服務器端,你可以使用以下代碼:

1
import { getSession } from 'next-auth/react'

然後在API路由或getServerSideProps({req})中使用它:

1
const session = await getSession({ req })

這就是我使用NextAuth進行非常基本的身份驗證設置的方法。

NextAuth包提供了非常完整的功能,並提供了許多選項和自定義選擇,詳情請參考https://next-auth.js.org

tags: [“Next.js”, “NextAuth”, “Email Authentication”, “JWT”, “PostgreSQL”]