Skip to content
Auth

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.

支持的授权类型 #

🌐 Supported grant types

Supabase Auth 支持两种 OAuth 2.1 授权类型:

🌐 Supabase Auth supports two OAuth 2.1 grant types:

  1. 使用 PKCE 的授权码 (authorization_code) - 用于获取初始访问令牌
  2. 刷新令牌 (refresh_token) - 用于在无需重新认证的情况下获取新的访问令牌

带 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:

  1. 客户端发起授权 - 第三方应用将用户重定向到 Supabase Auth 的授权端点
  2. Supabase 验证并重定向 - Supabase Auth 验证 OAuth 参数并将用户重定向到你配置的授权 URL
  3. 用户认证和授权 - 你的前端会检查用户是否已登录,显示同意界面,并处理批准或拒绝
  4. 授权码已发放 - Supabase Auth 会生成一个短期有效的授权码,并重定向回客户端
  5. 代码交换 - 客户端用代码换取令牌
  6. 已授予访问权限 - 客户端收到访问令牌、刷新令牌和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-js OAuth 方法处理认证和同意
  • 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)
2
function generateCodeVerifier() {
3
const array = new Uint8Array(32)
4
crypto.getRandomValues(array)
5
return base64URLEncode(array)
6
}
7
8
// Create code challenge from verifier
9
async 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
}
15
16
function base64URLEncode(buffer) {
17
return btoa(String.fromCharCode(...buffer))
18
.replace(/\+/g, '-')
19
.replace(/\//g, '_')
20
.replace(/=/g, '')
21
}
22
23
// Generate and store verifier (you'll need it later)
24
const codeVerifier = generateCodeVerifier()
25
sessionStorage.setItem('code_verifier', codeVerifier)
26
27
// Generate challenge to send in authorization request
28
const codeChallenge = await generateCodeChallenge(codeVerifier)

步骤 2:授权请求 #

🌐 Step 2: Authorization request

客户端会把用户重定向到你的授权端点,并带上以下参数:

🌐 The client redirects the user to your authorization endpoint with the following parameters:

1
https://<project-ref>.supabase.co/auth/v1/oauth/authorize?
2
response_type=code
3
&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 令牌中

🌐 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:

  1. 提取 authorization_id - 从 URL 查询参数中获取 authorization_id
  2. 获取授权详情 - 调用 supabase.auth.oauth.getAuthorizationDetails(authorization_id) 来获取有关 OAuth 客户端和请求参数的信息
  3. 检查用户身份验证 - 验证用户是否已登录;如果没有,重定向到你的登录页面(保留完整的授权路径,包括 authorization_id)。登录成功后,将用户重定向回带有相同 authorization_id 查询参数的授权路径
  4. 显示同意屏幕 - 向用户展示有关请求客户端的信息(名称、重定向 URI、权限范围)
  5. 处理用户决定 - 当用户批准或拒绝时:
    • 打电话给 supabase.auth.oauth.approveAuthorization(authorization_id) 批准
    • 调用 supabase.auth.oauth.denyAuthorization(authorization_id) 拒绝
    • 将用户重定向到返回的 redirect_url URL

这是一个使用 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:

1
https://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:

1
https://client-app.com/callback?
2
error=access_denied
3
&error_description=The+user+denied+the+authorization+request
4
&state=<state-from-request>

错误参数允许客户端向用户显示相关的错误信息:

🌐 The error parameters allow clients to display relevant error messages to users:

参数描述
error错误代码(例如,access_deniedinvalid_requestserver_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:

1
curl -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):

1
curl -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>'

机密客户(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:

1
curl -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 storage
2
const codeVerifier = sessionStorage.getItem('code_verifier')
3
4
// --- Public clients (token_endpoint_auth_method: none) ---
5
const 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
})
18
19
// --- Confidential clients (token_endpoint_auth_method: client_secret_basic) ---
20
const 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
})
33
34
// --- Confidential clients (token_endpoint_auth_method: client_secret_post) ---
35
const 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
})
49
50
const 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_tokenOpenID 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": 1735815600
20
}
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)

1
curl -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)

1
curl -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)

1
curl -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)
2
async 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
})
14
15
if (!response.ok) {
16
throw new Error('Failed to refresh token')
17
}
18
19
return await response.json()
20
}
21
22
// Confidential clients (token_endpoint_auth_method: client_secret_basic)
23
async 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
})
35
36
if (!response.ok) {
37
throw new Error('Failed to refresh token')
38
}
39
40
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
}

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.

身份证令牌 #

🌐 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:

1
curl '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": true
5
}

使用 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:

1
https://<project-ref>.supabase.co/auth/v1/.well-known/openid-configuration
2
https://<project-ref>.supabase.co/auth/v1/.well-known/oauth-authorization-server

这些端点返回有关以下内容的元数据:

🌐 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.

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:

1
https://<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:

1
import { createRemoteJWKSet, jwtVerify } from 'jose'
2
3
const JWKS = createRemoteJWKSet(
4
new URL('https://<project-ref>.supabase.co/auth/v1/.well-known/jwks.json')
5
)
6
7
async 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 payload
14
} catch (error) {
15
console.error('Token verification failed:', error)
16
return null
17
}
18
}

要验证什么 #

🌐 What to validate

总是核实:

🌐 Always verify:

  1. 签名 - Token 由 Supabase Auth 签署
  2. 发行者 (iss) - 与你的项目网址匹配
  3. 观众 (aud) - 是 authenticated
  4. 过期 (exp) - 令牌未过期
  5. 客户 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:

1
const { data: grants, error } = await supabase.auth.oauth.getUserGrants()
2
3
if (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:

1
const { error } = await supabase.auth.oauth.revokeGrant(clientId)
2
3
if (error) {
4
console.error('Error revoking access:', error)
5
} else {
6
console.log('Access revoked successfully')
7
}

撤销访问权限后:

🌐 After revoking access:

  • 该客户端的所有刷新令牌已被删除
  • 用户需要重新授权应用才能再次获取访问权限

要查看完整的 API 参考,请参见 supabase-js 中的 OAuth 方法

🌐 For complete API reference, see the OAuth methods in supabase-js.

下一步 #

🌐 Next steps