[Next.js] Next-auth
NextAuth.js 是用於 Next.js 應用程式的完整開源身份驗證解決方案。
- 支援 OAuth 1.0、1.0A、2.0 和 OpenID Connect
- 內建支持許多 sign-in 服務(Google 、 Apple 、 Twitter 、Github),也支援電子郵件/無密碼身份驗證
- 推廣使用無密碼登入機制
- 靈活數據管理,可以不使用數據庫,也可以選擇使用 MySQL, MariaDB, Postgres, SQL Server, MongoDB 以及 SQLite。
- 默認安全,默認 Cookie 機制,也支援 JSON Web Tokens 和 database sessions
如何使用
npm install next-auth
新增一個 API 路由 (Next.js 13.2 版本以上)
- 根據 authOptions 初始化 API Route(範例 code 如下)
import GoogleProvider from "next-auth/providers/google";
import NextAuth from "next-auth";
import type { AuthOptions } from "next-auth";
const authOptions: AuthOptions = {
// Configure one or more authentication providers
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID as string,
clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
}),
// ...add more providers here
],
pages: {
signIn: "/signin",
},
session: { strategy: "jwt" },
secret: process.env.NEXTAUTH_SECRET,
};
const handler = NextAuth(authOptions);
export { handler as GET, handler as POST };
providers: 可以放置許多授權服務(Google、FB)
pages: 代表 NextAuth.js 提供各種驗證方法的路徑位置,自定義身份驗證和授權過程中不同頁面的路徑。使用此配置時,請確保這些頁面實際存在。
session: 選擇您希望如何儲存使用者 Session,默認值是 jwt。 登入後可以在瀏覽器 cookies 中看到 token 的設置, CSRF token 是一種 XSS 防護措施,確保提交給 Server 的請求是合法的,並且真的來自用戶。

secret: 對 tokens 及 cookies 進行加密的密碼
在專案建立 .env 檔
GOOGLE_ID=<YOUR_CLIENT_ID_HERE>
GOOGLE_SECRET=<YOUR_CLIENT_SECRET_HERE>
NEXTAUTH_SECRET=<YOUR_NEXTAUTH_SECRET>
在 Client 端 取得 Session 的方法
useSession()Hook 是檢查某人是否登入的最簡單方法。 需確保將其<SessionProvider>添加到pages/_app.js- required 為 true 代表此頁面受到保護,需要登入才能進入該頁面, onUnauthenticated 代表使用者在登入後將重新導向至哪個 URL
- status: 若 required 為 true 時,status 只能是
loadingorauthenticated
'use client'
import { useSession } from 'next-auth/react'
import { redirect } from 'next/navigation'
const ClientProtectPage = () => {
const { data: session, status } = useSession({
required: true,
onUnauthenticated() {
// The user is not authenticated, handle it here.
redirect('/signin?callbackUrl=/protected/client')
}
})
return (
<section className='py-24'>
<div className='container'>
<h1 className='text-2xl font-bold'>
This is a <span className='text-emerald-500'>client-side</span>{' '}
protected page
</h1>
<h2 className='mt-4 font-medium'>You are logged in as:</h2>
<p className='mt-4'>{session?.user?.name}</p>
</div>
</section>
)
}
export default ClientProtectPage
在 Server 端取得 session
在 Server Side 一樣可以使 getSession 取得 session,但為了避免額外的 fetch 調用,官方建議還是使用 getServerSession()。 而專案中使用的場景是在 getCurrentUser 這個 action 中,取得 session 的同時也取得用戶的基本資料:
import { getServerSession } from 'next-auth/next'
import { redirect } from 'next/navigation'
import { authOptions } from '../../api/auth/[...nextauth]/route'
const ServerProtectedPage = async () => {
const session = await getServerSession(authOptions)
if (!session) {
redirect('/signin?callbackUrl=/protected/server')
}
return (
<section className='py-24'>
<div className='container'>
<h1 className='text-2xl font-bold'>
This is a <span className='text-emerald-500'>server-side</span>{' '}
protected page
</h1>
<h2 className='mt-4 font-medium'>You are logged in as:</h2>
<p className='mt-4'>{session?.user?.name}</p>
</div>
</section>
)
}
export default ServerProtectedPage
Bonus: 認識 OAuth2.0
OAuth 是一個開發標準(Open Standard )用來處理有關「授權」(Authorization)相關的行為。授權是代表授予某應用程式存取他人個資的能力的過程。
OAuth 2.0 定義了四種 Role
Authorization Code Grant Type Roles
1. Resource Owner (user): 資源擁有者,同時也是 應用程式 的用戶2. Application(Client) : 使用者正在準備使用的 web app,存取屬於使用者的受保護資源的應用程式, 像是 Server、手機或其他裝置( = 第三方的 應用程式,也可以是內部對外的應用程式)3. Authorization Server (核發及授權 access token 的 service): 授權伺服器,使用者授權 Client 後,用於發送 Access token 的伺服器,所有的權限資訊、安全資訊都在這個伺服器上管理4. Resource Server (一般指 API service): 資源伺服器,屬於使用者的受保護資源(像是文章、照片、個人隱私資料…等等)
用戶登錄:當 user 在瀏覽器裡面打開 第三方應用程式,並選擇使用 Google 帳戶登錄時,他們將被 redirect 到 Google 的登錄頁面。 第三方應用程式(Client)要求存取用戶的資源,但不需要知道密碼。
受權請求:在 Google 登錄頁面上,第三方應用程式 將向 user 發出授權請求,該請求會列出應用程式要訪問的 scope(例如,用戶的 Google 個人資料)。
如果用戶同意,他們將授予應用程序對其 Google 帳戶的訪問權限。身份驗證和授予授權:授權伺服器接收身份驗證和授權授予。 p.s. 用戶授權給第三方應用程式,這通常在授權伺服器(Authorization Server)上完成。(e.g. 在 google / fb 的 Authorization Server 上完成 )
發送受權碼:在用戶受權後,Google 將生成一個授權碼(Authorization Code),並將其返回給您的應用程式。
用受權碼請求 Token: p.s. 授權伺服器向第三方應用程式發送一個特殊的訪問令牌(Access Token)。
提供存取 Token:授權伺服器驗證授權代碼並頒發存取令牌。
使用存取 token 請求資源:第三方應用程式 使用該 token 來向 Google 的 API 發送請求,以獲得用戶的個人資料或執行其他操作。
p.s. 第三方應用程式使用訪問令牌來向 resource server 拿受保護的資源,而不需密碼。訪問資源:如果 token 有效,則資源伺服器傳回使用者授權應用程式接收的資源。
簡單來說: 資源擁有者(Resource Owner)在使用第三方應用程式(Application)時,透過授權伺服器(Authorization Server)授予第三方應用程式(Application)在資源伺服器(Resource Server)存取某些資源的權限。
解決問題
- 傳統的 Client-Server 架構裡, 傳統方法需要和三方應用程式共享密碼,但是在 OAuth 2.0 解決了這些問題
- OAuth 引入一個認證層 (authorization layer)
- NextAuth.js 中的身份驗證提供者是 OAuth 定義,允許您的用戶使用他們喜歡的現有帳號登錄。可以使用 Oauth Provider (e.g Github, Twitter, Google, etc...) 或是 自定義的 Oauth Provider 建立
參考資料
- https://next-auth.js.org/
- https://zhuanlan.zhihu.com/p/380561372
- https://liveblocks.io/blog/how-to-add-google-authentication-to-your-nextjs-liveblocks-app-with-nextauthjs
- https://www.telerik.com/blogs/how-to-implement-google-authentication-nextjs-app-using-nextauth
- https://developers.google.com/identity/protocols/oauth2?hl=zh-tw
- https://ithelp.ithome.com.tw/articles/10328911