JSON 网页令牌 (JWT)
Information on how best to use JSON Web Tokens with Supabase
一个 JSON Web Token 是一种数据结构,以字符串形式表示,通常包含关于用户的身份和授权信息。它会编码关于其有效期的信息,并使用加密密钥进行签名,以防止被篡改。
🌐 A JSON Web Token is a type of data structure, represented as a string, that usually contains identity and authorization information about a user. It encodes information about its lifetime and is signed with a cryptographic key to make it tamper-resistant.
Supabase Auth 会在用户保持登录状态的整个期间,为每个用户会话不断发放新的 JWT。查看关于 会话 的完整指南,了解如何根据你的需求定制这个过程。
🌐 Supabase Auth continuously issues a new JWT for each user session, for as long as the user remains signed in. Check the comprehensive guide on Sessions to find out how you can tailor this process for your needs.
JWT 为行级安全提供了基础。每个 Supabase 产品都能够在使用 Postgres 策略和角色授权访问项目数据之前,安全地解码并验证它收到的 JWT 的有效性。
🌐 JWTs provide the foundation for Row Level Security. Each Supabase product is able to securely decode and verify the validity of a JWT it receives before using Postgres policies and roles to authorize access to the project's data.
Supabase 提供了一个全面的系统来管理用来创建和验证 JSON Web 令牌的 JWT 签名密钥。
🌐 Supabase provides a comprehensive system of managing JWT Signing Keys used to create and verify JSON Web Tokens.
介绍 #
🌐 Introduction
JWT 是具有以下结构的字符串:
🌐 JWTs are strings that have the following structure:
1<header>.<payload>.<signature>每一部分都是一个经过 Base64-URL 编码的 JSON 字符串,或者用于签名的字节。
🌐 Each part is a string of Base64-URL encoded JSON, or bytes for the signature.
标题
1{2 "typ": "JWT",3 "alg": "<HS256 | ES256 | RS256>",4 "kid": "<unique key identifier>"5}提供一些关于字符串的基本识别信息,说明它的类型 typ、可以用来验证数据的加密算法 alg,以及可选的在验证时应使用的唯一密钥标识。
🌐 Gives some basic identifying information about the string, indicating its type typ, the cryptographic algorithm alg that can be used to verify the data, and optionally the unique key identifier that should be used when verifying it.
有效载荷
1{2 "iss": "https://project_id.supabase.co/auth/v1",3 "exp": 12345678,4 "sub": "<user ID>",5 "role": "authenticated",6 "email": "someone@example.com",7 "phone": "+15552368"8 // ...9}提供关于用户(或由令牌表示的其他实体)的标识信息(称为“声明”)。通常,JWT传递的信息可能是用户能访问的内容(称为访问令牌)或用户身份(称为ID令牌)。你可以使用自定义访问令牌钩子来添加、删除或更改令牌中的声明。有几个声明是很重要的:
🌐 Provides identifying information (called "claims") about the user (or other entity) that is represented by the token. Usually a JWT conveys information about what the user can access (then called Access Token) or who the user is (then called ID Token). You can use a Custom Access Token Hook to add, remove or change claims present in the token. A few claims are important:
| 声明 | 描述 |
|---|---|
iss | 标识发布令牌的服务器。如果你在这个 URL 后面加上 /.well-known/jwks.json,你就可以获取用于验证令牌的公钥。 |
exp | 设置一个时间限制,超过这个时间即使令牌签名正确也不再可信,会被视为过期。 |
sub | 意思是 主题,是令牌所代表用户的唯一 ID。 |
role | 在应用行级安全策略时使用的 Postgres 角色。 |
| ... | 其他所有声明都可以快速访问个人信息,而无需查询数据库或向认证服务器发送请求。 |
签名
使用共享密钥或公钥加密的数字签名。签名的目的是在不依赖数据库访问、Auth 服务器的活跃状态或性能的情况下验证 <header>.<payload> 字符串的真实性。验证签名时,避免自己实现算法,而是依赖 supabase.auth.getClaims() 或你所用语言的其他高质量 JWT 验证库。
🌐 A digital signature using a shared secret or public-key cryptography. The purpose of the signature is to verify the authenticity of the <header>.<payload> string without relying on database access, liveness or performance of the Auth server. To verify the signature avoid implementing the algorithms yourself and instead rely on supabase.auth.getClaims(), or other high-quality JWT verification libraries for your language.
Supabase 和 JWT #
🌐 Supabase and JWTs
Supabase 会在这些情况下为你创建 JWT:
🌐 Supabase creates JWTs in these cases for you:
- 在使用 Supabase Auth 时,每个用户在保持登录状态时都会生成一个访问令牌(JWT)。这些令牌有效期很短,所以随着用户与 Supabase API 的交互,它们会不断被重新发放。
- 在使用可发布或秘密 API 密钥时即时处理。每个 API 密钥都会被转换成一个短期有效的 JWT,然后用它来授权访问你的数据。通常无法直接获取这些短期令牌。
除了创建 JWT,Supabase 还可以通过 第三方认证 功能接受来自其他认证服务器的 JWT,或者接受通过导入的 JWT 签名密钥 在外部生成的 JWT。
🌐 In addition to creating JWTs, Supabase can also accept JWTs from other authentication servers via the Third-Party Auth feature or ones that have been minted externally via an imported JWT Signing Key.
使用自定义或第三方 JWT #
🌐 Using custom or third-party JWTs
supabase.auth.getClaims() 方法仅适用于 Supabase Auth 签发的 JWT。如果你使用自己导入的密钥生成 JWT,验证可能会失败。我们强烈建议你使用你所用语言的 JWT 验证库,根据你在 JWT 中添加的声明来验证这类 JWT。
🌐 The supabase.auth.getClaims() method is meant to be used only with JWTs issued by Supabase Auth. If you mint your own JWTs using a key you've imported, the verification may fail. We strongly recommend using a JWT verification library for your language to verify this type of JWT based on the claims you're adding in them.
你的 Supabase 项目在 Authorization: Bearer <jwt> 头中接受 JWT。如果你使用 Supabase 客户端库,它会为你处理这个。
🌐 Your Supabase project accepts a JWT in the Authorization: Bearer <jwt> header. If you're using the Supabase client library, it does this for you.
如果你已经在使用 Supabase Auth,当用户登录时,他们的访问令牌 JWT 会自动管理,并在每次 API 调用时为你发送。
🌐 If you are already using Supabase Auth, when a user is signed in, their access token JWT is automatically managed and sent for you with every API call.
如果你想从第三方认证提供商发送 JWT,或者使用你导入的 JWT 签名密钥自己生成的 JWT,你可以通过 accessToken 选项传递给客户端库。
🌐 If you wish to send a JWT from a Third-Party Auth provider, or one you made yourself by using a JWT signing key you imported, you can pass it to the client library using the accessToken option.
1import { createClient } from '@supabase/supabase-js'23const supabase = createClient(4 'https://<supabase-project>.supabase.co',5 'SUPABASE_PUBLISHABLE_KEY',6 {7 accessToken: async () => {8 return '<your JWT here>'9 },10 }11)过去有人建议在 Supabase 客户端上设置自定义头部,用 Authorization 头部包含你的自定义 JWT。但现在不推荐这样做了,因为这种方式灵活性较差,而且当与 Supabase Auth 的用户会话结合使用时容易引起混淆。
🌐 In the past there was a recommendation to set custom headers on the Supabase client with the Authorization header including your custom JWT. This is no longer recommended as it's less flexible and causes confusion when combined with a user session from Supabase Auth.
验证来自 Supabase 的 JWT #
🌐 Verifying a JWT from Supabase
如果你无法使用 Supabase 客户端库,下面的方法可以帮助你安全地验证由 Supabase 发出的 JWT。
🌐 If you're not able to use the Supabase client libraries, the following can be used to help you securely verify JWTs issued by Supabase.
Supabase Auth 为每个 Supabase 项目提供一个 JSON Web Key 集合 URL:
🌐 Supabase Auth exposes a JSON Web Key Set URL for each Supabase project:
1GET https://project-id.supabase.co/auth/v1/.well-known/jwks.json它会返回一个 JWKS 对象,其中包含一个或多个非对称的 JWT 签名密钥(仅包括它们的公钥)。请注意,如果你没有使用非对称的 JWT 签名密钥,这个端点是不会返回任何密钥的。
🌐 Which responds with JWKS object containing one or more asymmetric JWT signing keys (only their public keys). Be aware that this endpoint does not return any keys if you are not using asymmetric JWT signing keys.
1{2 "keys": [3 {4 "kid": "<match with kid from JWT header>",5 "alg": "<match with alg from JWT header>",6 "kty": "<RSA|EC|OKP>",7 "key_ops": ["verify"]8 // public key fields9 }10 ]11}这个端点是直接从认证服务器提供的,但也会被 Supabase Edge 额外缓存 10 分钟,无论你在哪里进行验证,都能显著加快访问速度。注意缓存过期时间很重要,以免不小心拒绝了有效的用户访问令牌。我们建议在创建备用签名密钥或撤销之前使用过的密钥时,至少等待 20 分钟。
🌐 This endpoint is served directly from the Auth server, but is also additionally cached by the Supabase Edge for 10 minutes, significantly speeding up access to this data regardless of where you're performing the verification. It's important to be aware of the cache expiry time to prevent unintentionally rejecting valid user access tokens. We recommend waiting at least 20 minutes when creating a standby signing key, or revoking a previously used key.
确保你的应用不要长时间缓存这些数据,因为这可能会让撤销变得困难。如果你确实缓存了,确保在更换签名密钥时提供一种清理缓存的方法,以避免无意中拒绝有效的用户访问令牌。
🌐 Make sure that you do not cache this data for longer in your application, as it might make revocation difficult. If you do, make sure to provide a way to purge this cache when rotating signing keys to avoid unintentionally rejecting valid user access tokens.
下面是一个如何使用 jose TypeScript JWT 验证库 来处理 Supabase JWT 的示例:
🌐 Below is an example of how to use the jose TypeScript JWT verification library with Supabase JWTs:
1import { createRemoteJWKSet, jwtVerify } from 'jose'23const PROJECT_JWKS = createRemoteJWKSet(4 new URL('https://project-id.supabase.co/auth/v1/.well-known/jwks.json')5)67/**8 * Verifies the provided JWT against the project's JSON Web Key Set.9 */10async function verifyProjectJWT(jwt: string) {11 return jwtVerify(jwt, PROJECT_JWKS)12}使用共享的秘密签名密钥进行验证 #
🌐 Verifying with a shared secret signing key
如果你的项目使用共享密钥(HS256)签名密钥,我们建议始终通过向认证服务器发送类似这样的请求来直接验证用户访问令牌:
🌐 If your project is using a shared secret (HS256) signing key, we recommend always verifying a user access token directly with the Auth server by sending a request like so:
1GET https://project-id.supabase.co/auth/v1/user2apikey: publishable key3Authorization: Bearer <JWT>如果服务器返回 HTTP 200 OK,那么 JWT 是有效的,否则就无效。
🌐 If the server responds with HTTP 200 OK, the JWT is valid, otherwise it is not.
因为认证服务器只在你项目指定的区域运行,并没有全球分布,所以根据你执行检查的地点,这个检查可能会很慢。尽量不要从运行在边缘的服务器或函数上做这种检查,最好是路由到与你项目在同一区域的服务器。
🌐 Because the Auth server runs only in your project's specified region and is not globally distributed, doing this check can be quite slow depending on where you're performing the check. Avoid doing checks like this from servers or functions running on the edge, and prefer routing to a server within the same geographical region as your project.
如果你使用共享密钥(HS256)作为签名密钥,你可能想用共享密钥来验证。我们强烈不推荐这种做法。
🌐 If you are using a shared secret (HS256) signing key, you may wish to verify using the shared secret. We strongly recommend against this approach.
使用用共享密钥签名的 JWT 几乎没有什么好处。虽然它在计算上更高效,手动编写验证也更简单,但使用这种方法可能会让你的项目数据暴露在重大安全漏洞或弱点中。
🌐 There is almost no benefit from using a JWT signed with a shared secret. Although it's computationally more efficient and verification is simpler to code by hand, using this approach can expose your project's data to significant security vulnerabilities or weaknesses.
考虑一下以下内容:
🌐 Consider the following:
- 使用共享密钥可能会让保持符合 SOC2、PCI-DSS、ISO27000、HIPAA 等安全合规框架变得更困难。
- 如果一个共享的秘密落入恶意行为者手中,他们可以用它来冒充你的用户,或让自己访问敏感的操作或数据。
- 很难发现或确定共享的秘密何时或如何被提供给了恶意行为者。
- 考虑一下谁可能会偶然接触到共享密钥:系统、员工、设备(以及它们的磁盘加密和漏洞修补状态)。
- 一个恶意行为者可以在很久以后使用共享密钥,所以即使现在没有被入侵的证据,也不意味着你的数据是安全的。
- 在公开可用的源代码中,比如你的网站、前端、移动应用包或其他可执行文件中,意外泄露共享密钥是很容易发生的。尤其是当你不小心把密钥放在以
NEXT_PUBLIC_、VITE_、PUBLIC_或其他由 Web 框架约定的前缀开头的环境变量中时,这种情况更容易发生。 - 轮换共享密钥可能需要小心协调,以避免你的应用停机。
查看你的语言的 JWT 验证库,了解如何安全地验证使用共享密钥(HS256)签名的 JWT。我们强烈建议按照上面描述的方法依赖认证服务器,或者改用基于公钥加密(RSA、椭圆曲线)的不同签名密钥。
🌐 Check the JWT verification libraries for your language on how to securely verify JWTs signed with a shared secret (HS256) signing key. We strongly recommend relying on the Auth server as described above, or switching to a different signing key based on public key cryptography (RSA, Elliptic Curves) instead.
资源 #
🌐 Resources
- JWT 调试器: https://jwt.io/
- JWT 签名密钥
- JWT 声明参考 - Supabase Auth 使用的所有 JWT 声明完整参考
- API 密钥