Skip to main content

[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 版本以上)

  1. 根據 authOptions 初始化 API Route(範例 code 如下)
/app/api/auth/[...nextauth]/route.ts

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 };
  1. providers: 可以放置許多授權服務(Google、FB)

  2. pages: 代表 NextAuth.js 提供各種驗證方法的路徑位置,自定義身份驗證和授權過程中不同頁面的路徑。使用此配置時,請確保這些頁面實際存在。

  3. session: 選擇您希望如何儲存使用者 Session,默認值是 jwt。 登入後可以在瀏覽器 cookies 中看到 token 的設置, CSRF token 是一種 XSS 防護措施,確保提交給 Server 的請求是合法的,並且真的來自用戶。

  4. secret: 對 tokens 及 cookies 進行加密的密碼


在專案建立 .env 檔

GOOGLE_ID=<YOUR_CLIENT_ID_HERE>
GOOGLE_SECRET=<YOUR_CLIENT_SECRET_HERE>
NEXTAUTH_SECRET=<YOUR_NEXTAUTH_SECRET>

在 Client 端 取得 Session 的方法

  1. useSession()Hook 是檢查某人是否登入的最簡單方法。 需確保將其 <SessionProvider> 添加到 pages/_app.js
  2. required 為 true 代表此頁面受到保護,需要登入才能進入該頁面, onUnauthenticated 代表使用者在登入後將重新導向至哪個 URL
  3. status: 若 required 為 true 時,status 只能是 loading or authenticated
'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): 資源伺服器,屬於使用者的受保護資源(像是文章、照片、個人隱私資料…等等)
  1. 用戶登錄:當 user 在瀏覽器裡面打開 第三方應用程式,並選擇使用 Google 帳戶登錄時,他們將被 redirect 到 Google 的登錄頁面。 第三方應用程式(Client)要求存取用戶的資源,但不需要知道密碼。

  2. 受權請求:在 Google 登錄頁面上,第三方應用程式 將向 user 發出授權請求,該請求會列出應用程式要訪問的 scope(例如,用戶的 Google 個人資料)。
    如果用戶同意,他們將授予應用程序對其 Google 帳戶的訪問權限。

  3. 身份驗證和授予授權:授權伺服器接收身份驗證和授權授予。 p.s. 用戶授權給第三方應用程式,這通常在授權伺服器(Authorization Server)上完成。(e.g. 在 google / fb 的 Authorization Server 上完成 )

  4. 發送受權碼:在用戶受權後,Google 將生成一個授權碼(Authorization Code),並將其返回給您的應用程式。

  5. 用受權碼請求 Token: p.s. 授權伺服器向第三方應用程式發送一個特殊的訪問令牌(Access Token)。

  6. 提供存取 Token:授權伺服器驗證授權代碼並頒發存取令牌。

  7. 使用存取 token 請求資源第三方應用程式 使用該 token 來向 Google 的 API 發送請求,以獲得用戶的個人資料或執行其他操作。
    p.s. 第三方應用程式使用訪問令牌來向 resource server 拿受保護的資源,而不需密碼。

  8. 訪問資源:如果 token 有效,則資源伺服器傳回使用者授權應用程式接收的資源。

tip

簡單來說: 資源擁有者(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 建立

參考資料