Note | NextAuth & OIDC Practical Example
這篇文章是 SSO Introduction 的延續,我們已經了解了 SSO 與 OIDC 的理論基礎, 現在我們將透過 NextAuth 與一個實作範例(Keycloak + Next.js + Spring Boot)來看看這些概念如何實現
實際的範例可以參考 OIDC-Example GitHub Repository 在這個 Repo 中,我實作了一個更完整的 SSO 流程,包含了前端、後端與 IdP 的整合與登出應該有的機制, 如 Token Refresh、前端與後端的安全性驗證。
1. Why NextAuth?
NextAuth.js 讓開發者不用自己手刻 OIDC 流程,只需要透過簡單的設定就能完成 SSO 登入,並且自動處理安全性驗證與 Session 管理。
在上一篇文章中,我們提到實作 OIDC 需要處理複雜的 Redirect、Token Exchange 以及各式各樣的安全性驗證(iss, aud, exp, nonce 等)。 對於前端開發者來說,如果每一項都要手刻,不僅耗時且容易產生安全漏洞。
NextAuth.js (現已更名為 Auth.js) 正是為了簡化這一切而生。它是一個為 Next.js 量身打造的驗證解決方案, 內建支持多種 OAuth/OIDC Provider (如 Google, GitHub, Keycloak),並且自動幫我們處理了:
- 前端與 IdP 之間的導向流程
- 回調後的 Token 交換 (Back-channel communication)
- Session 的建立與管理 (可選 JWT 或 Database 模式)
- 安全性標頭與 CSRF 防護
2. OIDC Practical Walkthrough: The “OIDC-Example” Project
為了讓大家更有體感,我們準備了一個包含三個組件的練習專案:
- Identity Provider (IdP): 使用 Keycloak (基於 Docker 運行)
- Frontend (RP): 使用 React + Next.js + NextAuth
- Resource Server (Backend): 使用 Spring Boot (Java)
2.1 NextAuth Configuration (The Frontend)
在 Next.js 中,我們透過定義 [...nextauth]/route.ts 來設定 OIDC Provider。以下是連接 Keycloak 的關鍵代碼:
import NextAuth, { NextAuthOptions } from "next-auth";
import KeycloakProvider from "next-auth/providers/keycloak";
export const authOptions: NextAuthOptions = {
providers: [
KeycloakProvider({
clientId: process.env.KEYCLOAK_CLIENT_ID!,
clientSecret: process.env.KEYCLOAK_CLIENT_SECRET!,
issuer: "http://localhost:8081/realms/demo", // Keycloak server URL
authorization: { params: { scope: "openid profile email" } },
}),
GoogleProvider({
clientId: process.env.GOOGLE_CLIENT_ID!,
clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
}),
],
callbacks: {
// Step 1: Save the Access Token from Keycloak into the encrypted JWT cookie upon successful login
async jwt({ token, account }) {
if (account) {
token.accessToken = account.access_token;
}
return token;
},
// Step 2: Expose the Access Token to the frontend session so it can be used for API requests
async session({ session, token }) {
session.accessToken = token.accessToken as string;
return session;
},
},
};
const handler = NextAuth(authOptions);
providers: 透過這個 Array 來宣告你的 NextAuth 將要使用的登入方式,這裡我們使用 Keycloak、 Google 兩種方式- 以 Keycloak 為例應該要帶入
clientId,clientSecret,issuer,以及authorization
- 以 Keycloak 為例應該要帶入
callbacks: 這裡可以定義登入後的回調函式,每種 callback 都有不同的用途jwtcallback 會在登入成功後被呼叫,我們可以在這裡把 Keycloak 回傳的access_token存入sessioncallback 會在前端呼叫getSession()時被呼叫,我們可以把access_token從 JWT Cookie 取出,並放入 Session 物件中
要了解一個 Callback Context 裡面有哪些屬性可以使用,最好直接去看 Source Code,官方文件只會列出部分屬性 JWTCallbackContext
NextAuth 已經內建了 Authorization Code Flow 所需要的 /api/auth/signin Endpoint,
點擊後會引導使用者至 Keycloak 登入,成功後帶回 code 並在後端換取 id_token 與 access_token。
如果你要自己寫 OIDC 的登入流程,就要自己實作各種的 Endpoint 與 Token 交換,例如
/api/auth/callback
2.2 Backend Security (The Resource Server)
當 Frontend 拿到 access_token 後,它會將其放入 HTTP Header (Authorization: Bearer <token>) 來請求後端數據。
Spring Boot 端的配置如下:
@Configuration
@EnableWebSecurity
public class SecurityConfig {
@Bean
public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
http
.authorizeHttpRequests(auth -> auth
.requestMatchers("/api/public/**").permitAll()
.anyRequest().authenticated()
)
.oauth2ResourceServer(oauth2 -> oauth2
.jwt(Customizer.withDefaults())
);
return http.build();
}
}
後端不需要自己保存使用者密碼,它只需要信任 IdP 發出的公鑰 (JWKS),驗證傳入的 JWT 是否合法即可。這就是 SSO 中「權限驗證外包」的具體展現。
3. Key Concepts Re-visited in Practice
在實作這個範例時,有幾個細節對應到了理論篇:
- Front-channel vs Back-channel:
- 前端跳轉到 Keycloak 登入頁面是 Front-channel。
- NextAuth 伺服器端拿到
code後,在後端呼叫 Keycloak/token端點是 Back-channel。
- Scope:
- 在設定中我們要求了
scope: "openid profile email",這決定了id_token裡面會包含哪些 User Claim。
- 在設定中我們要求了
- JWT Verification:
- Spring Boot 後端透過
issuer-uri自動向 Keycloak 抓取.well-known/openid-configuration,進而找到jwks_uri來驗證 Token 內容。
- Spring Boot 後端透過
4. Summary
透過 NextAuth,我們可以在極短的時間內建構出符合安全標準的 SSO 登入流程。它不僅隱藏了 OIDC 的複雜實作,還提供了靈活的 Hook (如 jwt 與 session callbacks) 讓我們能根據需求處理 Token。
下一步,建議讀者可以嘗試在同一個 Keycloak Realm 下接入第二個不同的應用程式,體驗「在應用 A 登入後,應用 B 無須再輸入密碼」的無縫體驗。
延伸閱讀
Last Edit
06-11-2026 14:00