OAuth 2.1 流程
Supabase Auth 实现了带有 OpenID Connect(OIDC)的 OAuth 2.1,支持带 PKCE 的授权码流程和刷新令牌流程。本指南详细讲解了这些流程的工作方式。
🌐 Supabase Auth implements OAuth 2.1 with OpenID Connect (OIDC), supporting the authorization code flow with PKCE and refresh token flow. This guide explains how these flows work in detail.
本指南解释了用于 第三方客户端应用 的 OAuth 2.1 流程,这些应用需要通过你的 Supabase 项目进行身份验证。这些流程需要自定义实现,在 @supabase/supabase-js 库中不可用。supabase-js 库是用于通过 Supabase Auth 作为身份提供者进行身份验证,而不是用于构建你自己的 OAuth 服务器。
🌐 This guide explains the OAuth 2.1 flows for third-party client applications that authenticate with your Supabase project. These flows require custom implementation and are not available in the @supabase/supabase-js library. The supabase-js library is for authenticating with Supabase Auth as an identity provider, not for building your own OAuth server.
支持的授权类型 #
🌐 Supported grant types
Supabase Auth 支持两种 OAuth 2.1 授权类型:
🌐 Supabase Auth supports two OAuth 2.1 grant types:
- 使用 PKCE 的授权码 (
authorization_code) - 用于获取初始访问令牌 - 刷新令牌 (
refresh_token) - 用于在无需重新认证的情况下获取新的访问令牌
像 client_credentials 或 password 这样的其他授权类型不被支持。
🌐 Other grant types like client_credentials or password are not supported.
带 PKCE 的授权码流程 #
🌐 Authorization code flow with PKCE
带 PKCE(用于代码交换的证明密钥)的授权码流程是推荐的 OAuth 客户端流程,适用于所有类型的应用,包括单页应用、移动应用和服务器端应用。
🌐 The authorization code flow with PKCE (Proof Key for Code Exchange) is the recommended flow for all OAuth clients, including single-page applications, mobile apps, and server-side applications.
它是怎么运作的 #
🌐 How it works
这个流程包括几个步骤:
🌐 The flow consists of several steps:
- 客户端发起授权 - 第三方应用将用户重定向到 Supabase Auth 的授权端点
- Supabase 验证并重定向 - Supabase Auth 验证 OAuth 参数并将用户重定向到你配置的授权 URL
- 用户认证和授权 - 你的前端会检查用户是否已登录,显示同意界面,并处理批准或拒绝
- 授权码已发放 - Supabase Auth 会生成一个短期有效的授权码,并重定向回客户端
- 代码交换 - 客户端用代码换取令牌
- 已授予访问权限 - 客户端收到访问令牌、刷新令牌和ID令牌
流程图 #
🌐 Flow diagram
这是完整授权码流程的可视化表示:
🌐 Here's a visual representation of the complete authorization code flow:
1┌─────────────┐ ┌──────────────────┐ ┌──────────────────┐2│ │ │ │ │ │3│ Client │ │ Your Auth UI │ │ Supabase Auth │4│ App │ │ (Frontend) │ │ │5│ │ │ │ │ │6└──────┬──────┘ └────────┬─────────┘ └────────┬─────────┘7 │ │ │8 │ 1. Generate PKCE params │9 │ (code_verifier, code_challenge) │10 │ │ │11 │ 2. Redirect to /oauth/authorize with code_challenge │12 ├───────────────────────────────────────────────────────────────>│13 │ │ │14 │ │ 3. Validate params & redirect │15 │ │ to authorization_path │16 │ │<────────────────────────────────┤17 │ │ │18 │ │ 4. getAuthorizationDetails() │19 │ ├────────────────────────────────>│20 │ │ Return client info │21 │ │<────────────────────────────────┤22 │ │ │23 │ │ 5. User login & consent │24 │ │ │25 │ │ 6. approveAuthorization() │26 │ ├────────────────────────────────>│27 │ │ Return redirect_url with code │28 │ │<────────────────────────────────┤29 │ │ │30 │ 7. Redirect to client callback with code │31 │<───────────────────────────────────────────────────────────────┤32 │ │ │33 │ 8. Exchange code for tokens (POST /oauth/token) │34 │ with code_verifier │35 ├───────────────────────────────────────────────────────────────>│36 │ │ │37 │ 9. Return tokens (access, refresh, ID) │38 │<───────────────────────────────────────────────────────────────┤39 │ │ │40 │ 10. Access resources with access_token │41 │ │ │42 │ 11. Refresh tokens (POST /oauth/token with refresh_token) │43 ├───────────────────────────────────────────────────────────────>│44 │ │ │45 │ 12. Return new tokens │46 │<───────────────────────────────────────────────────────────────┤47 │ │ │重点:
- 第三方客户端会将用户重定向到 Supabase Auth 的授权端点(而不是直接到你的界面)
- Supabase Auth 会验证 OAuth 参数并重定向到 你的授权路径
- 你的前端界面使用
supabase-jsOAuth 方法处理认证和同意 - Supabase Auth 处理所有后台 OAuth 逻辑(代码生成、令牌发放)
第1步:生成PKCE参数 #
🌐 Step 1: Generate PKCE parameters
在启动流程之前,客户端必须生成 PKCE 参数:
🌐 Before initiating the flow, the client must generate PKCE parameters:
1// Generate a random code verifier (43-128 characters)2function generateCodeVerifier() {3 const array = new Uint8Array(32)4 crypto.getRandomValues(array)5 return base64URLEncode(array)6}78// Create code challenge from verifier9async function generateCodeChallenge(verifier) {10 const encoder = new TextEncoder()11 const data = encoder.encode(verifier)12 const hash = await crypto.subtle.digest('SHA-256', data)13 return base64URLEncode(new Uint8Array(hash))14}1516function base64URLEncode(buffer) {17 return btoa(String.fromCharCode(...buffer))18 .replace(/\+/g, '-')19 .replace(/\//g, '_')20 .replace(/=/g, '')21}2223// Generate and store verifier (you'll need it later)24const codeVerifier = generateCodeVerifier()25sessionStorage.setItem('code_verifier', codeVerifier)2627// Generate challenge to send in authorization request28const codeChallenge = await generateCodeChallenge(codeVerifier)步骤 2:授权请求 #
🌐 Step 2: Authorization request
客户端会把用户重定向到你的授权端点,并带上以下参数:
🌐 The client redirects the user to your authorization endpoint with the following parameters:
1https://<project-ref>.supabase.co/auth/v1/oauth/authorize?2 response_type=code3 &client_id=<client-id>4 &redirect_uri=<configured-redirect-uri>5 &state=<random-state>6 &code_challenge=<code-challenge>7 &code_challenge_method=S256必填参数 #
🌐 Required parameters
| 参数 | 描述 |
|---|---|
response_type | 授权码流程必须是 code |
client_id | 注册时的客户端 ID |
redirect_uri | 必须与注册的重定向 URI 完全匹配 |
code_challenge | 生成的代码挑战 |
code_challenge_method | 必须是 S256(SHA-256) |
可选参数 #
🌐 Optional parameters
| 参数 | 描述 |
|---|---|
state | 随机字符串,用于防止 CSRF 攻击(强烈推荐) |
scope | 用空格分隔的权限列表(例如 openid email profile phone)。请求的权限将包含在访问令牌中,并控制 UserInfo 端点返回的信息。未提供时默认权限为 email。如果请求了 openid 权限,响应中会包含 ID 令牌 |
nonce | 用于防重放攻击的随机字符串。如果提供,将包含在 ID 令牌中 |
总是包含一个 state 参数来防止 CSRF 攻击。生成一个随机字符串,存储在会话存储中,并在用户返回时验证是否匹配。
🌐 Always include a state parameter to protect against CSRF attacks. Generate a random string, store it in session storage, and verify it matches when the user returns.
第3步:用户身份验证和同意 #
🌐 Step 3: User authentication and consent
在收到授权请求后,Supabase Auth 会验证 OAuth 参数(client_id、redirect_uri、PKCE 等),然后将用户重定向到你配置的授权路径(例如 https://example.com/oauth/consent?authorization_id=<id>)。
🌐 After receiving the authorization request, Supabase Auth validates the OAuth parameters (client_id, redirect_uri, PKCE, etc.) and then redirects the user to your configured authorization path (e.g., https://example.com/oauth/consent?authorization_id=<id>).
这个网址会包含一个 authorization_id 查询参数,用来识别这个授权请求。
🌐 The URL will contain an authorization_id query parameter that identifies this authorization request.
你在授权路径上的前端应用应该:
🌐 Your frontend application at the authorization path should:
- 提取 authorization_id - 从 URL 查询参数中获取
authorization_id - 获取授权详情 - 调用
supabase.auth.oauth.getAuthorizationDetails(authorization_id)来获取有关 OAuth 客户端和请求参数的信息 - 检查用户身份验证 - 验证用户是否已登录;如果没有,重定向到你的登录页面(保留完整的授权路径,包括
authorization_id)。登录成功后,将用户重定向回带有相同authorization_id查询参数的授权路径 - 显示同意屏幕 - 向用户展示有关请求客户端的信息(名称、重定向 URI、权限范围)
- 处理用户决定 - 当用户批准或拒绝时:
- 打电话给
supabase.auth.oauth.approveAuthorization(authorization_id)批准 - 调用
supabase.auth.oauth.denyAuthorization(authorization_id)拒绝 - 将用户重定向到返回的
redirect_urlURL
- 打电话给
这是一个使用 supabase-js 的前端实现。Supabase Auth 在你调用批准/拒绝方法后处理所有后端的 OAuth 逻辑(生成授权码、验证请求等)。
🌐 This is a frontend implementation using supabase-js. Supabase Auth handles all the backend OAuth logic (generating authorization codes, validating requests, etc.) after you call the approve/deny methods.
查看 入门指南 获取完整的实现示例。
🌐 See the Getting Started guide for complete implementation examples.
步骤4:已发放授权码 #
🌐 Step 4: Authorization code issued
如果用户批准访问,Supabase Auth 会带着授权码重定向回客户端的重定向 URI:
🌐 If the user approves access, Supabase Auth redirects back to the client's redirect URI with an authorization code:
1https://client-app.com/callback?2 code=<authorization-code>3 &state=<state-from-request>授权码是:
🌐 The authorization code is:
- 短期有效 - 有效期为10分钟
- 一次性 - 只能兑换一次
- 绑定到 PKCE - 只能用正确的代码验证器交换
如果用户拒绝访问,Supabase Auth 会在查询参数中重定向并带上错误信息:
🌐 If the user denies access, Supabase Auth redirects with error information in query parameters:
1https://client-app.com/callback?2 error=access_denied3 &error_description=The+user+denied+the+authorization+request4 &state=<state-from-request>错误参数允许客户端向用户显示相关的错误信息:
🌐 The error parameters allow clients to display relevant error messages to users:
| 参数 | 描述 |
|---|---|
error | 错误代码(例如,access_denied、invalid_request、server_error) |
error_description | 可读的错误描述,用来说明出了什么问题 |
state | 原始请求中的状态参数(用于防止 CSRF 攻击) |
步骤5:令牌兑换 #
🌐 Step 5: Token exchange
客户端通过向令牌端点发送 POST 请求来用授权码换取令牌。客户端如何进行身份验证取决于它的 token_endpoint_auth_method(在客户端注册时设置)。
🌐 The client exchanges the authorization code for tokens by making a POST request to the token endpoint. How the client authenticates depends on its token_endpoint_auth_method (set during client registration).
公共客户(token_endpoint_auth_method: none#
🌐 Public clients (token_endpoint_auth_method: none)
公共客户端只在请求体中发送 client_id,没有任何密钥:
🌐 Public clients send only the client_id in the request body with no secret:
1curl -X POST 'https://<project-ref>.supabase.co/auth/v1/oauth/token' \2 -H 'Content-Type: application/x-www-form-urlencoded' \3 -d 'grant_type=authorization_code' \4 -d 'code=<authorization-code>' \5 -d 'client_id=<client-id>' \6 -d 'redirect_uri=<redirect-uri>' \7 -d 'code_verifier=<code-verifier>'机密客户(token_endpoint_auth_method: client_secret_basic#
🌐 Confidential clients (token_endpoint_auth_method: client_secret_basic)
这是机密客户端的默认设置。凭证通过 Authorization 头使用 HTTP 基本认证(base64 编码的 client_id:client_secret)发送:
🌐 This is the default for confidential clients. Credentials are sent via the Authorization header using HTTP Basic authentication (base64-encoded client_id:client_secret):
1curl -X POST 'https://<project-ref>.supabase.co/auth/v1/oauth/token' \2 -H 'Content-Type: application/x-www-form-urlencoded' \3 -u '<client-id>:<client-secret>' \4 -d 'grant_type=authorization_code' \5 -d 'code=<authorization-code>' \6 -d 'redirect_uri=<redirect-uri>' \7 -d 'code_verifier=<code-verifier>'cURL 中的 -u 标志会自动对凭据进行编码并设置 Authorization: Basic <base64(client_id:client_secret)> 头。如果你没用 cURL,就必须自己对 client_id:client_secret 字符串进行 base64 编码。
🌐 The -u flag in cURL automatically encodes the credentials and sets the Authorization: Basic <base64(client_id:client_secret)> header. If you're not using cURL, you must base64-encode the client_id:client_secret string yourself.
机密客户(token_endpoint_auth_method: client_secret_post#
🌐 Confidential clients (token_endpoint_auth_method: client_secret_post)
凭证作为表单参数随请求体发送:
🌐 Credentials are sent as form parameters in the request body:
1curl -X POST 'https://<project-ref>.supabase.co/auth/v1/oauth/token' \2 -H 'Content-Type: application/x-www-form-urlencoded' \3 -d 'grant_type=authorization_code' \4 -d 'code=<authorization-code>' \5 -d 'client_id=<client-id>' \6 -d 'client_secret=<client-secret>' \7 -d 'redirect_uri=<redirect-uri>' \8 -d 'code_verifier=<code-verifier>'JavaScript 示例 #
🌐 Example in JavaScript
1// Retrieve the code verifier from storage2const codeVerifier = sessionStorage.getItem('code_verifier')34// --- Public clients (token_endpoint_auth_method: none) ---5const response = await fetch(`https://<project-ref>.supabase.co/auth/v1/oauth/token`, {6 method: 'POST',7 headers: {8 'Content-Type': 'application/x-www-form-urlencoded',9 },10 body: new URLSearchParams({11 grant_type: 'authorization_code',12 code: authorizationCode,13 client_id: '<client-id>',14 redirect_uri: '<redirect-uri>',15 code_verifier: codeVerifier,16 }),17})1819// --- Confidential clients (token_endpoint_auth_method: client_secret_basic) ---20const response = await fetch(`https://<project-ref>.supabase.co/auth/v1/oauth/token`, {21 method: 'POST',22 headers: {23 'Content-Type': 'application/x-www-form-urlencoded',24 Authorization: 'Basic ' + btoa('<client-id>:<client-secret>'),25 },26 body: new URLSearchParams({27 grant_type: 'authorization_code',28 code: authorizationCode,29 redirect_uri: '<redirect-uri>',30 code_verifier: codeVerifier,31 }),32})3334// --- Confidential clients (token_endpoint_auth_method: client_secret_post) ---35const response = await fetch(`https://<project-ref>.supabase.co/auth/v1/oauth/token`, {36 method: 'POST',37 headers: {38 'Content-Type': 'application/x-www-form-urlencoded',39 },40 body: new URLSearchParams({41 grant_type: 'authorization_code',42 code: authorizationCode,43 client_id: '<client-id>',44 client_secret: '<client-secret>',45 redirect_uri: '<redirect-uri>',46 code_verifier: codeVerifier,47 }),48})4950const tokens = await response.json()第6步:令牌响应 #
🌐 Step 6: Token response
成功后,Supabase Auth 会返回一个带有令牌的 JSON 响应:
🌐 On success, Supabase Auth returns a JSON response with tokens:
1{2 "access_token": "eyJhbGc...",3 "token_type": "bearer",4 "expires_in": 3600,5 "refresh_token": "MXff...",6 "scope": "openid email profile",7 "id_token": "eyJhbGc..."8}| 字段 | 描述 |
|---|---|
access_token | 用于访问资源的 JWT 访问令牌 |
token_type | 总是 bearer |
expires_in | 令牌有效时间(秒)(默认值:3600) |
refresh_token | 用于获取新访问令牌的令牌 |
scope | 授权请求中授予的权限范围 |
id_token | OpenID Connect ID 令牌(仅在授权请求中请求了 openid 权限时包含) |
访问令牌结构 #
🌐 Access token structure
访问令牌是包含标准 Supabase 声明以及特定 OAuth 声明的 JWT:
🌐 Access tokens are JWTs containing standard Supabase claims plus OAuth-specific claims:
1{2 "aud": "authenticated",3 "exp": 1735819200,4 "iat": 1735815600,5 "iss": "https://<project-ref>.supabase.co/auth/v1",6 "sub": "user-uuid",7 "email": "user@example.com",8 "phone": "",9 "app_metadata": {10 "provider": "email",11 "providers": ["email"]12 },13 "user_metadata": {},14 "role": "authenticated",15 "aal": "aal1",16 "amr": [17 {18 "method": "password",19 "timestamp": 173581560020 }21 ],22 "session_id": "session-uuid",23 "client_id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d"24}OAuth 特定声明 #
🌐 OAuth-specific claims
| 声明 | 描述 |
|---|---|
client_id | 获取此令牌的 OAuth 客户端 ID |
其他所有声明都遵循标准的 Supabase JWT 结构。
🌐 All other claims follow the standard Supabase JWT structure.
可用范围 #
🌐 Available scopes
当前支持以下范围:
🌐 The following scopes are currently supported:
| 范围 | 描述 |
|---|---|
openid | 启用 OpenID Connect。请求时,响应中会包含 ID 令牌。 |
email | 授权访问 email 和 email_verified 声明 |
profile | 授权访问个人资料信息(名称、头像等) |
phone | 授权访问 phone_number 和 phone_number_verified 声明 |
默认作用域: 当授权请求中未指定作用域时,默认作用域是 email。
Scopes 会影响 ID 令牌中包含的信息以及 UserInfo 端点返回的数据。所有 OAuth 访问令牌都可以完全访问用户数据(和普通会话令牌一样),还会额外包含 client_id 声明。使用带有 client_id 声明的行级安全策略来控制每个 OAuth 客户端可以访问哪些数据。
🌐 Scopes affect what information is included in ID tokens and returned by the UserInfo endpoint. All OAuth access tokens have full access to user data (same as regular session tokens), with the addition of the client_id claim. Use Row Level Security policies with the client_id claim to control which data each OAuth client can access.
当前不支持自定义作用域。 目前只能使用上面列出的标准作用域。未来的版本计划会支持自定义作用域,这样你就可以定义特定应用的权限和更精细的访问控制。
刷新令牌流程 #
🌐 Refresh token flow
刷新令牌让客户端可以在不需要用户重新认证的情况下获取新的访问令牌。
🌐 Refresh tokens allow clients to obtain new access tokens without requiring the user to re-authenticate.
什么时候刷新 #
🌐 When to refresh
客户端应在以下情况下刷新访问令牌:
🌐 Clients should refresh access tokens when:
- 访问令牌已过期(检查
exp声明) - 访问令牌即将过期(主动刷新)
- 一个 API 调用返回了 401 未授权错误
刷新请求 #
🌐 Refresh request
使用刷新令牌向令牌端点发起 POST 请求。客户端的认证方式和在token交换时一样,基于它的token_endpoint_auth_method。
🌐 Make a POST request to the token endpoint with the refresh token. The client authenticates the same way as during the token exchange, based on its token_endpoint_auth_method.
公共客户(token_endpoint_auth_method: none#
🌐 Public clients (token_endpoint_auth_method: none)
1curl -X POST 'https://<project-ref>.supabase.co/auth/v1/oauth/token' \2 -H 'Content-Type: application/x-www-form-urlencoded' \3 -d 'grant_type=refresh_token' \4 -d 'refresh_token=<refresh-token>' \5 -d 'client_id=<client-id>'机密客户(token_endpoint_auth_method: client_secret_basic#
🌐 Confidential clients (token_endpoint_auth_method: client_secret_basic)
1curl -X POST 'https://<project-ref>.supabase.co/auth/v1/oauth/token' \2 -H 'Content-Type: application/x-www-form-urlencoded' \3 -u '<client-id>:<client-secret>' \4 -d 'grant_type=refresh_token' \5 -d 'refresh_token=<refresh-token>'机密客户(token_endpoint_auth_method: client_secret_post#
🌐 Confidential clients (token_endpoint_auth_method: client_secret_post)
1curl -X POST 'https://<project-ref>.supabase.co/auth/v1/oauth/token' \2 -H 'Content-Type: application/x-www-form-urlencoded' \3 -d 'grant_type=refresh_token' \4 -d 'refresh_token=<refresh-token>' \5 -d 'client_id=<client-id>' \6 -d 'client_secret=<client-secret>'JavaScript 示例 #
🌐 Example in JavaScript
1// Public clients (token_endpoint_auth_method: none)2async function refreshAccessToken(refreshToken) {3 const response = await fetch(`https://<project-ref>.supabase.co/auth/v1/oauth/token`, {4 method: 'POST',5 headers: {6 'Content-Type': 'application/x-www-form-urlencoded',7 },8 body: new URLSearchParams({9 grant_type: 'refresh_token',10 refresh_token: refreshToken,11 client_id: '<client-id>',12 }),13 })1415 if (!response.ok) {16 throw new Error('Failed to refresh token')17 }1819 return await response.json()20}2122// Confidential clients (token_endpoint_auth_method: client_secret_basic)23async function refreshAccessTokenConfidential(refreshToken) {24 const response = await fetch(`https://<project-ref>.supabase.co/auth/v1/oauth/token`, {25 method: 'POST',26 headers: {27 'Content-Type': 'application/x-www-form-urlencoded',28 Authorization: 'Basic ' + btoa('<client-id>:<client-secret>'),29 },30 body: new URLSearchParams({31 grant_type: 'refresh_token',32 refresh_token: refreshToken,33 }),34 })3536 if (!response.ok) {37 throw new Error('Failed to refresh token')38 }3940 return await response.json()41}刷新回应 #
🌐 Refresh response
回应包含一个新的访问令牌,也可以包含一个新的刷新令牌:
🌐 The response contains a new access token and optionally a new refresh token:
1{2 "access_token": "eyJhbGc...",3 "token_type": "bearer",4 "expires_in": 3600,5 "refresh_token": "v1.MXff...",6 "scope": "openid email profile"7}刷新令牌可能会轮换(会发放一个新的刷新令牌)。每当提供新的刷新令牌时,请务必更新你存储的令牌。
🌐 Refresh tokens may be rotated (a new refresh token is issued). Always update your stored refresh token when a new one is provided.
OpenID 连接 (OIDC) #
🌐 OpenID Connect (OIDC)
Supabase 身份认证支持 OpenID Connect,这是一层建立在 OAuth 2.1 之上的身份验证层。
🌐 Supabase Auth supports OpenID Connect, an identity layer on top of OAuth 2.1.
只有在请求 openid 范围时才会包含 ID 令牌。 要接收 ID 令牌,请在授权请求的以空格分隔的范围列表中包含 openid。ID 令牌有效期为 1 小时。
身份证令牌 #
🌐 ID tokens
ID 令牌是包含用户身份信息的 JWT。它们由 Supabase Auth 签发,客户端可以进行验证。
🌐 ID tokens are JWTs that contain user identity information. They are signed by Supabase Auth and can be verified by clients.
ID 令牌中包含的声明取决于授权时请求的权限范围。例如,请求 openid email profile 会包含邮箱和个人资料相关的声明,而只请求 openid email 则只会包含邮箱相关的声明。
🌐 The claims included in the ID token depend on the scopes requested during authorization. For example, requesting openid email profile will include email and profile-related claims, while requesting only openid email will include only email-related claims.
示例 ID 令牌 #
🌐 Example ID token
1{2 "iss": "https://<project-ref>.supabase.co/auth/v1",3 "sub": "user-uuid",4 "aud": "client-id",5 "exp": 1735819200,6 "iat": 1735815600,7 "auth_time": 1735815600,8 "nonce": "random-nonce-from-request",9 "email": "user@example.com",10 "email_verified": true,11 "phone_number": "+1234567890",12 "phone_number_verified": false,13 "name": "John Doe",14 "picture": "https://example.com/avatar.jpg"15}标准 OIDC 声明 #
🌐 Standard OIDC claims
| 声明 | 描述 |
|---|---|
sub | 主题(用户ID) |
nonce | 授权请求中的随机数值(如果提供的话) |
email | 用户的电子邮件地址 |
email_verified | 邮箱是否已验证 |
phone_number | 用户的电话号码 |
phone_number_verified | 电话是否已验证 |
name | 用户的全名 |
picture | 用户的头像网址 |
用户信息端点 #
🌐 UserInfo endpoint
客户可以通过使用访问令牌调用 UserInfo 接口来获取用户信息:
🌐 Clients can retrieve user information by calling the UserInfo endpoint with an access token:
1curl 'https://<project-ref>.supabase.co/auth/v1/oauth/userinfo' \2 -H 'Authorization: Bearer <access-token>'返回的信息取决于访问令牌中授予的权限范围。例如:
🌐 The information returned depends on the scopes granted in the access token. For example:
使用 email 范围:
1{2 "sub": "user-uuid",3 "email": "user@example.com",4 "email_verified": true5}使用 email profile phone 范围:
1{2 "sub": "user-uuid",3 "email": "user@example.com",4 "email_verified": true,5 "phone_number": "+1234567890",6 "phone_number_verified": false,7 "name": "John Doe",8 "picture": "https://example.com/avatar.jpg"9}OIDC 发现 #
🌐 OIDC discovery
Supabase 身份验证提供了 OpenID Connect 和 OAuth 2.1 发现端点,用来描述它的功能:
🌐 Supabase Auth exposes OpenID Connect and OAuth 2.1 discovery endpoints that describe its capabilities:
1https://<project-ref>.supabase.co/auth/v1/.well-known/openid-configuration2https://<project-ref>.supabase.co/auth/v1/.well-known/oauth-authorization-server两个端点返回相同的元数据,可以互换使用。它们是为了兼容不同的 OAuth 和 OIDC 客户端而提供的,因为这些客户端可能会期望使用其中的一个。
🌐 Both endpoints return the same metadata and can be used interchangeably. They are provided for compatibility with different OAuth and OIDC clients that may expect one or the other.
这些端点返回有关以下内容的元数据:
🌐 These endpoints return metadata about:
- 可用端点(授权、令牌、用户信息、JWKS)
- 支持的授权类型和响应类型
- 支持的范围和声明
- 令牌签名算法
这可以让你自动与符合 OIDC 标准的库和工具集成。
🌐 This enables automatic integration with OIDC-compliant libraries and tools.
令牌验证 #
🌐 Token validation
第三方客户端应验证访问令牌,以确保它们是真实的且未被篡改。
🌐 Third-party clients should validate access tokens to ensure they're authentic and not tampered with.
推荐:使用非对称 JWT 签名密钥
对于 OAuth 实现,我们强烈建议使用非对称签名算法(RS256 或 ES256),而不是默认的 HS256。使用非对称密钥时,第三方客户端可以使用你 JWKS 端点的公钥来验证 JWT,而不需要访问你的 JWT 密钥。这样更安全、可扩展,而且符合 OAuth 的最佳实践。
🌐 For OAuth implementations, we strongly recommend using asymmetric signing algorithms (RS256 or ES256) instead of the default HS256. With asymmetric keys, third-party clients can validate JWTs using the public key from your JWKS endpoint without needing access to your JWT secret. This is more secure, scalable, and follows OAuth best practices.
了解如何在你的项目中配置非对称 JWT 签名密钥。
🌐 Learn how to configure asymmetric JWT signing keys in your project.
ID 令牌需要非对称签名算法
如果你请求 openid 范围来获取 ID 令牌,你的项目必须配置为使用非对称签名算法(RS256 或 ES256)。如果你的项目仍然使用默认的 HS256 对称算法,ID 令牌生成会失败并报错。这是 OpenID Connect 规范的安全要求。
🌐 If you request the openid scope to receive ID tokens, your project must be configured to use asymmetric signing algorithms (RS256 or ES256). ID token generation will fail with an error if your project is still using the default HS256 symmetric algorithm. This is a security requirement of the OpenID Connect specification.
JWKS 端点 #
🌐 JWKS endpoint
Supabase Auth 提供了一个 JSON Web Key Set (JWKS) 端点,包含用于令牌验证的公钥:
🌐 Supabase Auth exposes a JSON Web Key Set (JWKS) endpoint containing public keys for token verification:
1https://<project-ref>.supabase.co/auth/v1/.well-known/jwks.json示例回答:
🌐 Example response:
1{2 "keys": [3 {4 "kty": "RSA",5 "kid": "key-id",6 "use": "sig",7 "alg": "RS256",8 "n": "...",9 "e": "AQAB"10 }11 ]12}正在验证令牌 #
🌐 Validating tokens
使用 JWT 库来验证令牌:
🌐 Use a JWT library to verify tokens:
1import { createRemoteJWKSet, jwtVerify } from 'jose'23const JWKS = createRemoteJWKSet(4 new URL('https://<project-ref>.supabase.co/auth/v1/.well-known/jwks.json')5)67async function verifyAccessToken(token) {8 try {9 const { payload } = await jwtVerify(token, JWKS, {10 issuer: 'https://<project-ref>.supabase.co/auth/v1',11 audience: 'authenticated',12 })13 return payload14 } catch (error) {15 console.error('Token verification failed:', error)16 return null17 }18}要验证什么 #
🌐 What to validate
总是核实:
🌐 Always verify:
- 签名 - Token 由 Supabase Auth 签署
- 发行者 (
iss) - 与你的项目网址匹配 - 观众 (
aud) - 是authenticated - 过期 (
exp) - 令牌未过期 - 客户 ID (
client_id) - 与你的客户匹配(如果适用)
管理用户权限 #
🌐 Managing user grants
用户可以查看和管理他们授权访问自己账户的 OAuth 应用。这对于透明度和安全性很重要,让用户可以在需要时审查并撤销访问权限。
🌐 Users can view and manage the OAuth applications they've authorized to access their account. This is important for transparency and security, allowing users to audit and revoke access when needed.
查看已授权的应用 #
🌐 Viewing authorized applications
用户可以获取他们已授权的所有 OAuth 客户端的列表:
🌐 Users can retrieve a list of all OAuth clients they've authorized:
1const { data: grants, error } = await supabase.auth.oauth.getUserGrants()23if (error) {4 console.error('Error fetching grants:', error)5} else {6 console.log('Authorized applications:', grants)7}响应中包括每个授权 OAuth 客户端的详细信息:
🌐 The response includes details about each authorized OAuth client:
1[2 {3 "id": "grant-uuid",4 "client_id": "9a8b7c6d-5e4f-3a2b-1c0d-9e8f7a6b5c4d",5 "client_name": "My Third-Party App",6 "scopes": ["email", "profile"],7 "created_at": "2025-01-15T10:30:00.000Z",8 "updated_at": "2025-01-15T10:30:00.000Z"9 }10]撤销访问权限 #
🌐 Revoking access
用户可以随时撤销特定 OAuth 客户端的访问权限。一旦访问被撤销,该客户端的所有活跃会话和刷新令牌都会立即失效:
🌐 Users can revoke access for a specific OAuth client at any time. When access is revoked, all active sessions and refresh tokens for that client are immediately invalidated:
1const { error } = await supabase.auth.oauth.revokeGrant(clientId)23if (error) {4 console.error('Error revoking access:', error)5} else {6 console.log('Access revoked successfully')7}撤销访问权限后:
🌐 After revoking access:
- 该客户端的所有刷新令牌已被删除
- 用户需要重新授权应用才能再次获取访问权限
为你的用户建立一个设置页面
提供一个设置页面,让用户可以查看所有授权的应用,并撤销对他们不再信任或使用的应用的访问权限,是一个不错的做法。这可以增加透明度,让用户更好地掌控自己的数据。
🌐 It's a good practice to provide a settings page where users can view all authorized applications and revoke access to any they no longer trust or use. This increases transparency and gives users control over their data.
要查看完整的 API 参考,请参见 supabase-js 中的 OAuth 方法。
🌐 For complete API reference, see the OAuth methods in supabase-js.
下一步 #
🌐 Next steps
- 实现 MCP 身份验证 - 启用 AI 代理身份验证
- 使用 RLS 保护 - 控制 OAuth 客户端的数据访问
- 了解 JWT - 了解 Supabase 的令牌结构