Skip to content
Auth

使用 Keycloak 登录

要为你的项目启用 Keycloak 认证,你需要设置一个 Keycloak OAuth 应用,并将应用凭证添加到你的 Supabase 控制面板。

🌐 To enable Keycloak Auth for your project, you need to set up an Keycloak OAuth application and add the application credentials to your Supabase Dashboard.

概览 #

🌐 Overview

要开始使用 Keycloak,你可以通过以下命令在 Docker 容器中运行它:docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:latest start-dev

🌐 To get started with Keycloak, you can run it in a docker container with: docker run -p 8080:8080 -e KEYCLOAK_ADMIN=admin -e KEYCLOAK_ADMIN_PASSWORD=admin quay.io/keycloak/keycloak:latest start-dev

本指南假设你正在按照上面的命令在 Docker 容器中运行 Keycloak。

🌐 This guide will be assuming that you are running Keycloak in a docker container as described in the command above.

Keycloak OAuth 大致分为五个步骤:

🌐 Keycloak OAuth consists of five broad steps:

  • 在你指定的 Keycloak 域中创建一个新客户端。
  • 从“OpenID 端点配置”中获取 issuer。这将作为 Keycloak URL 使用。
  • 确保新客户端的“客户端协议”设置为 openid-connect,并且“访问类型”设置为“机密”。
  • 创建的客户端的 Client ID 会被用作 client id
  • 从凭证标签中获取 Secret,它将被用作 client secret
  • 把你应用的回调 URL 加到允许列表里。

访问你的 Keycloak 管理控制台 #

🌐 Access your Keycloak admin console

创建一个 Keycloak 域 #

🌐 Create a Keycloak realm

  • 一旦你登录到 Keycloak 控制台,就可以从侧边面板添加一个字段。默认字段应该叫做“Master”。
  • 在你添加了一个新的字段后,你可以从“OpenID 端点配置”端点获取 issuerissuer 将用作 Keycloak URL
  • 你可以在字段设置的“常规”标签下找到这个端点,或者访问http://localhost:8080/realms/my_realm_name/.well-known/openid-configuration

Add a Keycloak Realm.

创建一个 Keycloak 客户端 #

🌐 Create a Keycloak client

创建的客户端的“客户端 ID”将在你进行 API 调用以验证用户时用作 client_id

🌐 The "Client ID" of the created client will serve as the client_id when you make API calls to authenticate the user.

Add a Keycloak client

客户端设置 #

🌐 Client settings

在你成功创建客户端后,确保你设置以下内容:

🌐 After you've created the client successfully, ensure that you set the following settings:

  1. “客户端协议”应该设置为 openid-connect
  2. "访问类型" 应设置为 "机密"。
  3. “有效重定向 URI” 应该设置为:https://<project-ref>.supabase.co/auth/v1/callback

获取客户端ID,设置客户端协议和访问类型 设置重定向URI

获取客户端密钥 #

🌐 Obtain the client secret

当你进行 API 调用以验证用户时,这将作为 client_secret。在“凭据”标签下,Secret 的值将用作 client secret

🌐 This will serve as the client_secret when you make API calls to authenticate the user. Under the "Credentials" tab, the Secret value will be used as the client secret.

Obtain the client secret

在你的客户端应用中添加登录代码 #

🌐 Add login code to your client app

自从 Keycloak 22 版本起,必须传递 openid 范围。把它添加到 supabase.auth.signInWithOAuth() 方法中。

🌐 Since Keycloak version 22, the openid scope must be passed. Add this to the supabase.auth.signInWithOAuth() method.

当你的用户登录时,用 keycloak 作为 provider 调用 signInWithOAuth()

🌐 When your user signs in, call signInWithOAuth() with keycloak as the provider:

1
import { createClient } from '@supabase/supabase-js'
2
3
const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')
4
5
// ---cut---
6
async function signInWithKeycloak() {
7
const { data, error } = await supabase.auth.signInWithOAuth({
8
provider: 'keycloak',
9
options: {
10
scopes: 'openid',
11
},
12
})
13
}

以 PKCE 流程为例,比如在服务端认证中,你需要一个额外的步骤来处理代码交换。在调用 signInWithOAuth 时,提供一个指向回调路由的 redirectTo URL。这个重定向 URL 应该添加到你的 重定向允许列表 中。

🌐 For a PKCE flow, for example in Server-Side Auth, you need an extra step to handle the code exchange. When calling signInWithOAuth, provide a redirectTo URL which points to a callback route. This redirect URL should be added to your redirect allow list.

在浏览器中,signInWithOAuth 会自动重定向到 OAuth 提供商的认证端点,然后再重定向到你的端点。

🌐 In the browser, signInWithOAuth automatically redirects to the OAuth provider's authentication endpoint, which then redirects to your endpoint.

1
import { createClient, type Provider } from '@supabase/supabase-js';
2
const supabase = createClient('https://your-project-id.supabase.co', 'sb_publishable_...')
3
const provider = 'provider' as Provider
4
5
// ---cut---
6
await supabase.auth.signInWithOAuth({
7
provider,
8
options: {
9
redirectTo: `http://example.com/auth/callback`,
10
},
11
})

在回调端点,处理代码交换以保存用户会话。

🌐 At the callback endpoint, handle the code exchange to save the user session.

app/auth/callback/route.ts 创建一个新文件,并填入以下内容:

🌐 Create a new file at app/auth/callback/route.ts and populate with the following:

app/auth/callback/route.ts
1
import { NextResponse } from 'next/server'
2
3
// The client you created from the Server-Side Auth instructions
4
import { createClient } from '@/utils/supabase/server'
5
6
export async function GET(request: Request) {
7
const { searchParams, origin } = new URL(request.url)
8
const code = searchParams.get('code')
9
// if "next" is in param, use it as the redirect URL
10
let next = searchParams.get('next') ?? '/'
11
if (!next.startsWith('/')) {
12
// if "next" is not a relative URL, use the default
13
next = '/'
14
}
15
16
if (code) {
17
const supabase = await createClient()
18
const { error } = await supabase.auth.exchangeCodeForSession(code)
19
if (!error) {
20
const forwardedHost = request.headers.get('x-forwarded-host') // original origin before load balancer
21
const isLocalEnv = process.env.NODE_ENV === 'development'
22
if (isLocalEnv) {
23
// we can be sure that there is no load balancer in between, so no need to watch for X-Forwarded-Host
24
return NextResponse.redirect(`${origin}${next}`)
25
} else if (forwardedHost) {
26
return NextResponse.redirect(`https://${forwardedHost}${next}`)
27
} else {
28
return NextResponse.redirect(`${origin}${next}`)
29
}
30
}
31
}
32
33
// return the user to an error page with instructions
34
return NextResponse.redirect(`${origin}/auth/auth-code-error`)
35
}

当你的用户注销时,调用 signOut() 来将他们从浏览器会话中移除,并清除 localStorage 中的任何对象:

🌐 When your user signs out, call signOut() to remove them from the browser session and any objects from localStorage:

1
async function signOut() {
2
const { error } = await supabase.auth.signOut()
3
}

资源 #

🌐 Resources

  • 你可以在字段设置下找到 Keycloak OpenID 端点配置。Keycloak OpenID 端点配置