使用 Keycloak 登录
要为您的项目启用 Keycloak 身份验证,您需要设置一个 Keycloak OAuth 应用程序并将应用程序凭据添加到 Supabase 仪表板。
概述
要开始使用 Keycloak,您可以通过以下命令在 Docker 容器中运行它:docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:latest start-dev
本指南假设您正在按照上述命令在 Docker 容器中运行 Keycloak。
Keycloak OAuth 包含五个主要步骤:
- 在指定的 Keycloak 域中创建新客户端
- 从"OpenID 端点配置"获取
issuer
,这将用作Keycloak URL
- 确保新客户端的"客户端协议"设置为
openid-connect
,且"访问类型"设置为"confidential" - 所创建客户端的
Client ID
将用作client id
- 从凭据选项卡获取
Secret
,这将用作client secret
- 将应用程序的回调 URL 添加到允许列表中
访问 Keycloak 管理控制台
- 访问
http://localhost:8080
并点击"Administration Console"登录
创建 Keycloak 域
- 登录 Keycloak 控制台后,您可以从侧边栏添加域。默认域应命名为"Master"
- 添加新域后,您可以从"OpenID Endpoint Configuration"端点获取
issuer
,该issuer
将用作Keycloak URL
- 您可以在域设置的"General Tab"下找到此端点,或访问
http://localhost:8080/realms/my_realm_name/.well-known/openid-configuration
创建 Keycloak 客户端
所创建客户端的"Client ID"将作为您进行用户认证API调用时的client_id
参数。
客户端设置
成功创建客户端后,请确保配置以下设置:
- "Client Protocol"应设置为
openid-connect
- "Access Type"应设置为"confidential"
- "Valid Redirect URIs"应设置为:
https://<project-ref>.supabase.co/auth/v1/callback
获取客户端密钥
这将成为您进行用户认证API调用时的client_secret
参数。
在"Credentials"标签页下,Secret
值将用作客户端密钥。
在客户端应用中添加登录代码
自 Keycloak 22 版本起,必须传递 openid
作用域。请将此添加到 supabase.auth.signInWithOAuth()
方法中。
请确保在以下代码中使用正确的 supabase
客户端。
如果您未使用服务器端渲染或基于 cookie 的身份验证,可以直接使用 @supabase/supabase-js
中的 createClient
。如果您正在使用服务器端渲染,请参阅 服务器端身份验证指南,了解创建 Supabase 客户端的说明。
当用户登录时,调用 signInWithOAuth()
并将 provider
设为 keycloak
:
12345678async function () { const { , } = await ..({ : 'keycloak', : { : 'openid', }, })}
以 PKCE 流程为例,比如在服务器端身份验证中,您需要额外的步骤来处理代码交换。调用 signInWithOAuth
时,提供一个指向回调路由的 redirectTo
URL。此重定向 URL 应添加到您的重定向允许列表中。
在浏览器中,signInWithOAuth
会自动重定向到 OAuth 提供商的身份验证端点,然后再重定向到您的端点。
123456await ..({ , : { : `http://example.com/auth/callback`, },})
在回调端点,处理代码交换以保存用户会话。
在 app/auth/callback/route.ts
创建一个新文件,并填充以下内容:
app/auth/callback/route.ts
12345678910111213141516171819202122232425262728293031323334import { NextResponse } from 'next/server'// 您根据服务器端身份验证说明创建的客户端import { createClient } from '@/utils/supabase/server'export async function GET(request: Request) { const { searchParams, origin } = new URL(request.url) const code = searchParams.get('code') // 如果参数中有 "next",则将其用作重定向 URL let next = searchParams.get('next') ?? '/' if (!next.startsWith('/')) { // 如果 "next" 不是相对 URL,则使用默认值 next = '/' } if (code) { const supabase = await createClient() const { error } = await supabase.auth.exchangeCodeForSession(code) if (!error) { const forwardedHost = request.headers.get('x-forwarded-host') // 负载均衡器之前的原始来源 const isLocalEnv = process.env.NODE_ENV === 'development' if (isLocalEnv) { // 我们可以确定中间没有负载均衡器,因此无需关注 X-Forwarded-Host return NextResponse.redirect(`${origin}${next}`) } else if (forwardedHost) { return NextResponse.redirect(`https://${forwardedHost}${next}`) } else { return NextResponse.redirect(`${origin}${next}`) } } } // 将用户重定向到带有说明的错误页面 return NextResponse.redirect(`${origin}/auth/auth-code-error`)}
当用户登出时,调用 signOut() 将其从浏览器会话和 localStorage 中移除:
123async function signOut() { const { error } = await supabase.auth.signOut()}
资源
- 您可以在领域设置中找到 Keycloak 的 OpenID 端点配置。