配置自定义 OAuth/OIDC 提供者
Set up any OAuth 2.0 or OIDC-compatible identity provider for self-hosted Supabase.
本指南解释了如何向自托管的 Supabase 实例添加自定义 OAuth 2.0 或 OpenID Connect (OIDC) 身份提供者,以及如何通过 Auth 管理 API 管理它。想了解自定义提供者的工作原理以及它们支持哪些高级选项,请参见 自定义 OAuth/OIDC 提供者。
🌐 This guide explains how to add a custom OAuth 2.0 or OpenID Connect (OIDC) identity provider to a self-hosted Supabase instance and how to manage it through the Auth admin API. To learn how custom providers work and which advanced options they support, see Custom OAuth/OIDC Providers.
有两种供应商类型:
🌐 There are two provider types:
- OAuth 2.0:适用于通用的 OAuth 2.0 提供商,你需要手动提供授权、令牌和用户信息的端点。
- OIDC:适用于支持 OpenID Connect 发现的提供者。你只需提供发行者 URL,端点会自动解析。
提供者标识 #
🌐 Provider identifiers
每个自定义提供商标识必须以 custom: 前缀开头。标识长度为 2-50 个字符,可包含小写字母、数字、连字符和冒号。例如:
🌐 Every custom provider identifier must start with the custom: prefix. Identifiers are 2-50 characters, lowercase alphanumeric with hyphens and colons allowed. Examples:
custom:my-providercustom:github-enterprise
在你开始之前 #
🌐 Before you begin
你需要:
🌐 You need:
- 一个运行在发布版
0.8.1或更高版本的自托管 Supabase 实例,该版本包含 Supabase Authv2.196.0。参见 使用 Docker 自托管 和 更新你的自托管部署。 - 你项目的秘密密钥
SUPABASE_SECRET_KEY,来自你的.env文件。 API_EXTERNAL_URL设置为你的身份验证服务的公开可访问 URL,结尾为/auth/v1,例如https://<your-domain>/auth/v1。身份验证服务会根据这个值为自定义提供者生成 OAuth 回调 URL。
大多数 OAuth 提供商都会拒绝除 localhost 之外的 http:// 回调 URL,所以你的实例需要 HTTPS。设置说明请参见 配置反向代理和 HTTPS。
🌐 Most OAuth providers reject http:// callback URLs other than localhost, so your instance needs HTTPS. See Configure Reverse Proxy and HTTPS for setup instructions.
在向外部身份提供商注册你的应用时,请在提供商的设置中将以下 URL 添加为重定向 URI:
🌐 When registering your application with an external identity provider, add the following URL as the redirect URI in the provider's settings:
https://<your-domain>/auth/v1/callback可选认证配置 #
🌐 Optional Auth configuration
在身份验证服务中,自定义提供程序默认是启用的,因此无需对 docker-compose.yml 进行任何更改。以下环境变量可以调整默认设置:
🌐 Custom providers are enabled by default in the Auth service, so no changes to docker-compose.yml are required. The following environment variables adjust the defaults:
| 变量 | 描述 |
|---|---|
GOTRUE_CUSTOM_OAUTH_ENABLED | 设置为 "false" 来禁用自定义 OAuth/OIDC 提供者。默认值是 true。 |
GOTRUE_CUSTOM_OAUTH_MAX_PROVIDERS | 允许的自定义 OAuth/OIDC 提供者最大数量。默认值是 0,表示无限制。 |
GOTRUE_CUSTOM_OAUTH_EXTERNAL_URL | 用于构建自定义提供者回调 URL 的基础 URL。默认值是 API_EXTERNAL_URL 的值。 |
要更改其中的任何内容,请将变量添加到你 docker-compose.yml 中的 auth 服务:
🌐 To change any of these, add the variable to the auth service in your docker-compose.yml:
auth: environment: # ... existing variables ... GOTRUE_CUSTOM_OAUTH_MAX_PROVIDERS: 5然后重新创建 Auth 服务以使更改生效:
🌐 Then recreate the Auth service for the change to take effect:
sh run.sh recreate auth创建一个提供者 #
🌐 Create a provider
使用 Auth 管理 API 来创建提供者。你需要项目的密钥来进行认证。
🌐 Use the Auth admin API to create providers. You need your project's secret key for authentication.
这些 JavaScript 示例使用了一个通过密钥(SUPABASE_SECRET_KEY)创建的 supabase-js 客户端,这个客户端只能在受信任的服务器端环境中运行。
🌐 The JavaScript examples use a supabase-js client created with the secret key (SUPABASE_SECRET_KEY), which must only run in trusted server-side environments.
import { } from '@supabase/supabase-js'const = ('http://<your-domain>', 'your-supabase-secret-key')当你创建提供者时,认证服务会获取并验证提供者的端点,所以如果无法访问发行者或端点 URL,请求就会失败。
🌐 The Auth service fetches and validates the provider's endpoints when you create the provider, so the request fails if the issuer or endpoint URLs can't be reached.
OAuth 2.0 提供者 #
🌐 OAuth 2.0 provider
当你的身份提供者不支持 OpenID Connect 发现时,使用 OAuth 2.0 提供者。你必须明确提供授权、令牌和用户信息端点的 URL。
🌐 Use an OAuth 2.0 provider when your identity provider does not support OpenID Connect discovery. You must supply the authorization, token, and userinfo endpoint URLs explicitly.
const { data, error } = await supabase.auth.admin.customProviders.createProvider({ provider_type: 'oauth2', identifier: 'custom:my-oauth-provider', name: 'My OAuth Provider', client_id: 'your-client-id', client_secret: 'your-client-secret', authorization_url: 'https://provider.example.com/oauth/authorize', token_url: 'https://provider.example.com/oauth/token', userinfo_url: 'https://provider.example.com/oauth/userinfo', scopes: ['profile', 'email'],})OIDC 提供者 #
🌐 OIDC provider
当你的身份提供者支持 OpenID Connect 时,使用 OIDC 提供者。提供 issuer URL,发现文档、JWKS 和端点会自动解析。
🌐 Use an OIDC provider when your identity provider supports OpenID Connect. Supply the issuer URL and the discovery document, JWKS, and endpoints are resolved automatically.
const { data, error } = await supabase.auth.admin.customProviders.createProvider({ provider_type: 'oidc', identifier: 'custom:my-oidc-provider', name: 'My OIDC Provider', client_id: 'your-client-id', client_secret: 'your-client-secret', issuer: 'https://auth.example.com', scopes: ['openid', 'profile', 'email'],})OIDC 提供者有以下自动行为:
🌐 OIDC providers have the following automatic behavior:
- 发现文档是从
{issuer}/.well-known/openid-configuration获取的,或者在设置了discovery_url时从discovery_url获取。 openid范围总是包含的。如果在scopes数组中缺失,它会自动添加。- ID 令牌会根据提供者的 JWKS 进行验证,这个 JWKS 是从发现文档的
jwks_uri获取的。
核实提供者 #
🌐 Verify the provider
列出已配置的提供者以确认新提供者已保存:
🌐 List the configured providers to confirm the new provider was saved:
curl "http://<your-domain>/auth/v1/admin/custom-providers" \ -H "apikey: your-supabase-secret-key"响应中包含了你创建的提供者。用户现在可以通过你的应用使用它登录:
🌐 The response includes the provider you created. Users can now sign in with it from your app:
const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'custom:my-oidc-provider',})示例:电报 #
🌐 Example: Telegram
以下步骤将演示如何使用 Supabase Auth 设置 Telegram 登录。
🌐 The following steps walk through setting up Telegram sign-in with Supabase Auth.
在 v2.196.0 版本之前的 Supabase Auth 上,Telegram 登录失败,因为存在一个不支持的签名算法问题。
🌐 Telegram sign-in fails on Supabase Auth versions before v2.196.0 because of an unsupported signing algorithm issue.
第1步:创建一个电报机器人 #
🌐 Step 1: Create a Telegram bot
- 在 Telegram 上打开 @BotFather 并发送命令
/newbot。 - 按照提示为你的机器人选择一个名字和用户名。
第2步:注册重定向网址 #
🌐 Step 2: Register the redirect URL
-
在 Telegram 上打开 @BotFather 小程序,然后选择你在上一步创建的机器人。
-
选择 登录小部件 打开配置重定向 URI 的页面。
-
如果你看不到 OIDC 设置,点击“切换到 OpenID Connect 登录”。
-
点击 添加重定向 URI,然后输入你自托管的 Supabase 回调 URL:
https://<your-domain>/auth/v1/callback
这个屏幕还会显示你的客户端 ID 和客户端密钥。你会在下一步创建提供者时用到它们。
🌐 This screen also shows your Client ID and Client Secret. You use them to create the provider in the next step.
第3步:创建 Telegram 提供者 #
🌐 Step 3: Create the Telegram provider
从 登录小部件 屏幕复制客户端 ID 和客户端密钥,然后使用它们创建自定义的 Telegram OAuth 提供商:
🌐 Copy the Client ID and Client Secret from the Login Widget screen, then use them to create a custom Telegram OAuth provider:
const { data, error } = await supabase.auth.admin.customProviders.createProvider({ provider_type: 'oidc', identifier: 'custom:telegram', name: 'Telegram', client_id: 'your-client-id', client_secret: 'your-client-secret', issuer: 'https://oauth.telegram.org', scopes: ['openid', 'profile'], email_optional: true,})关于这些字段的一些说明:
🌐 A few notes on these fields:
identifier:可以是任何以custom:开头并遵循 identifier rules 的值。name:可以是你想要的任何值。email_optional:必须是true,因为 Telegram 不会返回邮箱地址。
profile 范围会返回用户的名称和头像。如果还想获取手机号,需要添加 phone 范围。有关所有可用范围的完整列表,请查看 Telegram 登录文档。
🌐 The profile scope returns the user's name and picture. To also receive the phone number, add the phone scope. For the full list of available scopes, see the Telegram Login docs.
第4步:用 Telegram 登录 #
🌐 Step 4: Sign in with Telegram
配置好提供商后,你现在可以从你的应用用 Telegram 登录了:
🌐 With the provider configured, you can now sign in with Telegram from your app:
const { data, error } = await supabase.auth.signInWithOAuth({ provider: 'custom:telegram',})步骤5:测试登录流程 #
🌐 Step 5: Test the sign-in flow
只有 Supabase 回调 URL 需要公开并通过 HTTPS 提供服务,因为身份提供者会重定向到那里。测试页面本身是一个静态 HTML 文件,可以在你的浏览器可以访问的任何地方运行,包括你的本地电脑。
🌐 Only the Supabase callback URL needs to be public and served over HTTPS, because that is where the identity provider redirects. The test page itself is a static HTML file that can run anywhere your browser can reach, including your local machine.
- 把下面的代码保存到
index.html。 - 在你自托管的 Supabase
.env文件中将SITE_URL设置为页面运行的 URL,例如http://localhost:3000,然后用sh run.sh recreate auth重新创建 Auth 服务。要允许多个 URL,可以把其他的添加到ADDITIONAL_REDIRECT_URLS。 - 通过
python -m http.server 3000启动一个简单的 HTTP 服务器来提供index.html。 - 打开你的浏览器,然后去
http://localhost:3000。
<!doctype html><html> <body> <h1>Supabase custom provider test</h1> <button id="loginBtn">Sign in with Telegram</button> <pre id="result"></pre> <script src="https://cdn.jsdelivr.net/npm/@supabase/supabase-js@2"></script> <script> document.addEventListener('DOMContentLoaded', function () { const SUPABASE_URL = 'https://<your-domain>' const SUPABASE_PUBLISHABLE_KEY = 'your-supabase-publishable-key' const supabase = window.supabase.createClient(SUPABASE_URL, SUPABASE_PUBLISHABLE_KEY) document.getElementById('loginBtn').addEventListener('click', async () => { const { error } = await supabase.auth.signInWithOAuth({ provider: 'custom:telegram', }) if (error) { document.getElementById('result').textContent = JSON.stringify(error, null, 2) } }) supabase.auth.onAuthStateChange((_event, session) => { if (session) { document.getElementById('result').textContent = JSON.stringify(session.user, null, 2) } }) }) </script> </body></html>点击按钮会让浏览器跳转到你的认证服务,然后它会重定向到 Telegram。在你在 Telegram 应用中确认登录后,Telegram 会把浏览器送回 https://<your-domain>/auth/v1/callback。认证服务完成交换后会重定向到 SITE_URL,在那里 supabase-js 接管会话,页面显示已登录的用户。
Telegram 不会返回电子邮件地址,所以用户对象中的 email 是空的。资料声称 Telegram 返回的 name、given_name、family_name 和 picture 都在 user_metadata 中,和 ID 令牌声明在一起,而 app_metadata.provider 是 custom:telegram。sub 声明是 Auth 用来在后续登录中匹配用户的稳定标识。
🌐 Telegram doesn't return an email address, so email is empty in the user object. The profile claims that Telegram returns, name, given_name, family_name, and picture, are in user_metadata together with the ID token claims, and app_metadata.provider is custom:telegram. The sub claim is the stable identifier that Auth uses to match the user on later sign-ins.
要测试不同的自定义提供者,把 provider 的值改成它的标识符。
🌐 To test a different custom provider, change the provider value to its identifier.
管理供应商 #
🌐 Manage providers
使用管理 API 来列出、更新和删除自定义提供者。自托管 Studio 不包含它们的界面。示例中使用 custom:my-provider 作为标识符。用你提供者的标识符替换它。
🌐 Use the admin API to list, update, and delete custom providers. Self-hosted Studio doesn't include a UI for them. The examples use custom:my-provider as the identifier. Replace it with the identifier of your provider.
列出提供商 #
🌐 List providers
// List all custom providersconst { data, error } = await supabase.auth.admin.customProviders.listProviders()// Filter by provider typeconst { data, error } = await supabase.auth.admin.customProviders.listProviders({ type: 'oidc',})更新供应商 #
🌐 Update a provider
只发送你想要更改的字段。未发送的字段会保留当前值,所以你可以只发送 client_secret 来轮换客户端密钥。provider_type 和 identifier 字段在创建提供者时就固定了,无法更新。
🌐 Send only the fields you want to change. Fields you leave out keep their current values, so you can rotate a client secret by sending client_secret alone. The provider_type and identifier fields are fixed when the provider is created and can't be updated.
const { data, error } = await supabase.auth.admin.customProviders.updateProvider( 'custom:my-provider', { name: 'Updated Provider Name', scopes: ['openid', 'profile', 'email'], })删除供应商 #
🌐 Delete a provider
const { data, error } = await supabase.auth.admin.customProviders.deleteProvider('custom:my-provider')有关 PKCE、授权参数和 OIDC 特定选项,请参见 高级配置。
🌐 For PKCE, authorization parameters, and OIDC-specific options, see Advanced configuration.
额外资源 #
🌐 Additional resources