From 2f31010a18490a7f529878c57767f2398ace43bc Mon Sep 17 00:00:00 2001 From: Miranda Limonczenko Date: Tue, 1 Sep 2026 14:40:53 -0700 Subject: [PATCH] docs: style edit for the API keys guide (#49795) ## I have read the [CONTRIBUTING.md](https://github.com/supabase/supabase/blob/master/CONTRIBUTING.md) file. YES ## What kind of change does this PR introduce? Docs update. Style only. ## What is the current behavior? The API keys guide has drifted from `WORD_LIST.md` and `CONTRIBUTING.md`. It also carries two defects: - The rotation steps tell you to replace the new key with the compromised one, rather than the reverse. - The secret key caution list opens with "Do not:" but several items read "Never use" and "Do not pass", which inverts them into the opposite instruction. ## What is the new behavior? Word-level edit. No section is added, moved, or reordered, so the restructure in the next PR of this stack lands as a readable set of moved lines. - Fix the reversed rotation instruction. - Rewrite the caution list so every item completes its "Don't:" stem. - Replace the Silicon Valley character names and trailing ellipses in the responsibility table. - Drop italics used for plain emphasis, parenthetical asides, `etc.`, `&`, the lint-flagged "easy", and existential sentence openers. - Replace "since" and "as" used for cause, and future tense used for current product behavior. ## Additional context PR 1 of 4. Base is `master`. ## Manual testing 1. Open [Understanding API keys](https://docs-git-docs-api-keys-style-edit-supabase.vercel.app/docs/guides/getting-started/api-keys) on the deploy preview. 2. Read the secret key caution list. Every item completes the "Don't:" stem. 3. Read "What to do if a secret key or `service_role` has been leaked or compromised". The order is: create the new key, then replace the compromised key with it. ## Summary by CodeRabbit * **Documentation** * Rewritten the API keys guide with clearer wording and improved structure. * Clarified how to access API keys through the Connect dialog and distinguished API keys from Supabase Auth. * Updated explanations of publishable and secret keys, including cautions, security best practices, and steps for responding to leaked keys. * Refined guidance on known limitations and compatibility differences. --- .../guides/getting-started/api-keys.mdx | 118 +++++++++--------- 1 file changed, 59 insertions(+), 59 deletions(-) diff --git a/apps/docs/content/guides/getting-started/api-keys.mdx b/apps/docs/content/guides/getting-started/api-keys.mdx index ee97ebbe184..e9278f6338a 100644 --- a/apps/docs/content/guides/getting-started/api-keys.mdx +++ b/apps/docs/content/guides/getting-started/api-keys.mdx @@ -8,33 +8,33 @@ Supabase gives you fine-grained control over which application components are al -In most cases, you can get the correct key from [the Project's **Connect** dialog](/dashboard/project/_?showConnect=true), but if you want a specific key, you can find all keys in the [**Settings > API Keys**](/dashboard/project/_/settings/api-keys/) section of the Dashboard: +In most cases, you can get the correct key from your project's [**Connect** dialog](/dashboard/project/_?showConnect=true). To pick a specific key, open the [**Settings > API Keys**](/dashboard/project/_/settings/api-keys/) section of the Dashboard. -API keys provide the first layer of authentication for data access. Auth then builds upon that. This chart covers the differences: +API keys provide the first layer of authentication for data access. Supabase Auth builds on top of that. This table covers the difference: -| Responsibility | Question | Answer | -| ---------------------------------- | ---------------------------------- | -------------------------------------------------- | -| API keys | **What** is accessing the project? | Web page, mobile app, server, Edge Function... | -| [Supabase Auth](/docs/guides/auth) | **Who** is accessing the project? | Monica, Jian Yang, Gavin, Dinesh, Laurie, Fiona... | +| Responsibility | Question | Answer | +| ---------------------------------- | ------------------------------ | ------------------------------------------------------- | +| API keys | What is accessing the project? | A web page, a mobile app, a server, or an Edge Function | +| [Supabase Auth](/docs/guides/auth) | Who is accessing the project? | An individual signed-in user | ## Overview -An API key authenticates an application component to give it access to Supabase services. An application component might be a web page, a mobile app, or a server. The API key _does not_ distinguish between users, only between applications. +An API key authenticates an application component to give it access to Supabase services. An application component might be a web page, a mobile app, or a server. An API key doesn't distinguish between users but between applications. -There are 4 types of API keys that you can use with Supabase: +Supabase supports four types of API keys: -| Type | Format | Privileges | Availability | Use | -| ---------------------------------------------------------- | ---------------------------------------------------------------- | ---------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| Publishable key | `sb_publishable_...` | Low | Platform | Safe to expose online: web page, mobile or desktop app, GitHub actions, CLIs, source code. | -| Secret keys | `sb_secret_...` | Elevated | Platform | **Only use in backend components of your app:** servers, already secured APIs (admin panels), [Edge Functions](/docs/guides/functions), microservices, etc. They provide _full access_ to your project's data, bypassing [Row Level Security](/docs/guides/database/postgres/row-level-security). | -| `anon` | JWT (long lived) | Low | Platform, CLI | Legacy version of publishable keys. | -| `service_role` | JWT (long lived) | Elevated | Platform, CLI | Legacy version of secret keys. | +| Type | Format | Privileges | Availability | Use | +| ---------------------------------------------------------- | ---------------------------------------------------------------- | ---------- | --------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Publishable key | `sb_publishable_...` | Low | Platform | Safe to expose online: web page, mobile or desktop app, GitHub actions, CLIs, source code. | +| Secret keys | `sb_secret_...` | Elevated | Platform | Only use in backend components of your app, such as servers, APIs with their own authorization checks, [Edge Functions](/docs/guides/functions), and microservices. They provide full access to your project's data, bypassing [Row Level Security](/docs/guides/database/postgres/row-level-security). | +| `anon` | JWT (long-lived) | Low | Platform, CLI | Legacy version of publishable keys. | +| `service_role` | JWT (long-lived) | Elevated | Platform, CLI | Legacy version of secret keys. | - + -Both key types work simultaneously. Creating publishable and secret keys adds them _alongside_ your existing `anon` and `service_role` keys without affecting them — your legacy keys keep working. They remain valid until you explicitly disable them in the [**Settings > API Keys**](/dashboard/project/_/settings/api-keys/) section of the Dashboard which is a separate step. See [Migrating to new API keys](/docs/guides/getting-started/migrating-to-new-api-keys) for the full process. +Both key types work simultaneously. Creating publishable and secret keys adds them alongside your existing `anon` and `service_role` keys without affecting them, so your legacy keys keep working. They remain valid until you disable them in the [**Settings > API Keys**](/dashboard/project/_/settings/api-keys/) section of the Dashboard, which is a separate step. See [Migrating to new API keys](/docs/guides/getting-started/migrating-to-new-api-keys) for the full process. @@ -42,100 +42,100 @@ Both key types work simultaneously. Creating publishable and secret keys adds th ## Publishable keys -Publishable keys identify the public components of your application. Public components run in environments where it is impossible to secure any secrets. These include: +Publishable keys identify the public components of your application. Public components run in environments where you can't keep a secret. These include: - Web pages, where the key is bundled in source code. - Mobile or desktop applications, where the key is bundled inside the compiled packages or executables. - CLI, scripts, tools, or other pre-built executables. -- Other publicly available APIs that return the key without prior additional authorization. +- Public APIs that return the key without requiring authorization first. These environments are always considered public because anyone can retrieve the key from the source code or build artifacts. ### Interaction with Supabase Auth -Using a publishable key does not mean that your user is anonymous. You can authenticate your application with the publishable key, while your user is authenticated (via Supabase Auth) with their personal JWT: +Using a publishable key doesn't mean your user is anonymous. Your application authenticates with the publishable key while your user authenticates separately through Supabase Auth with their own JWT: -| Key | User logged in via Supabase Auth | Postgres role used for RLS, etc. | -| --------------- | -------------------------------- | -------------------------------- | -| Publishable key | No | `anon` | -| Publishable key | Yes | `authenticated` | +| Key | User logged in via Supabase Auth | Postgres role | +| --------------- | -------------------------------- | --------------- | +| Publishable key | No | `anon` | +| Publishable key | Yes | `authenticated` | ### Security considerations -Publishable keys are not intended to protect from the following, since key retrieval is always possible from a public component: +Publishable keys aren't intended to protect against the following, because anyone can retrieve a key from a public component: - Static or dynamic code analysis and reverse engineering attempts. - Use of the Network inspector in the browser. - Cross-site request forgery, cross-site scripting, phishing attacks. - Man-in-the-middle attacks. -When using a publishable key, access to your project's data is guarded by Postgres via the built-in `anon` and `authenticated` roles. For full protection make sure: +When you use a publishable key, Postgres guards access to your project's data through the built-in `anon` and `authenticated` roles. For full protection, confirm that: - You have enabled Row Level Security on all tables. - You regularly review your Row Level Security policies for permissions granted to the `anon` and `authenticated` roles. -- You do not modify the role's attributes without understanding the changes you are making. +- You don't change the roles' attributes without understanding what the change does. -Your project's [Security Advisor](/dashboard/project/_/advisors/security) constantly checks for common security problems with the built-in Postgres roles. Make sure you carefully review each finding before dismissing it. +Your project's [Security Advisor](/dashboard/project/_/advisors/security) checks for common security problems with the built-in Postgres roles. Review each finding carefully before you dismiss it. ## What secret keys allow access to -Unlike publishable keys, secret keys allow elevated access to your project's data. It is meant to be used only in secure, developer-controlled components of your application, such as: +Unlike publishable keys, secret keys allow elevated access to your project's data. Use them only in secure, developer-controlled components of your application, such as: -- Servers that implement prior authorization themselves, such as Edge Functions, microservices, traditional or specialized web servers. +- Servers that run their own authorization checks, such as Edge Functions, microservices, and web servers. - Periodic jobs, queue processors, topic subscribers. -- Admin and back-office tools, with prior authorization checks only. +- Admin and back-office tools that run authorization checks first. - Data processing pipelines, such as for analytics, reports, backups, or database synchronization. -Never expose your secret keys publicly. Your data is at risk. **Do not:** +Exposing a secret key puts all of your project's data at risk. Don't: -- Add it to web pages, public documents, source code, bundle in executables or packages for mobile, desktop or CLI apps. -- Send over chat applications, email or SMS to your peers. -- Never use in a browser, even on `localhost`. -- Do not pass in URLs or query params, as these are often logged. -- Be careful passing them in request headers without prior log sanitization. -- Take extra care logging even potentially **invalid API keys**. Typos might reveal the real key in the future. -- Reveal, copy, use or manipulate on hardware devices without full disk encryption and which you do not directly own or control (such as public computers, friend's laptop, etc.) +- Add a secret key to a web page, a public document, source code, or a bundled mobile, desktop, or CLI package. +- Send one over chat, email, or SMS. +- Use one in a browser, even on `localhost`. +- Pass one in a URL or query parameter, because those are often logged. +- Pass one in a request header before you sanitize your logs. +- Log even an apparently invalid API key. A typo today can reveal the real key later. +- Reveal, copy, or use one on a device you don't own or control, or on a device without full-disk encryption. Public computers and borrowed laptops both count. -Ensure you handle them with care and using [secure coding practices](https://owasp.org/www-project-secure-coding-practices-quick-reference-guide/stable-en/). +Handle secret keys using [secure coding practices](https://owasp.org/www-project-secure-coding-practices-quick-reference-guide/stable-en/). -Secret keys authorize access to your project's data via the built-in `service_role` Postgres role. By design, this role has full access to your project's data. It also uses the [`BYPASSRLS` attribute](https://www.postgresql.org/docs/current/ddl-rowsecurity.html#:~:text=BYPASSRLS), skipping any and all Row Level Security policies you attach. +Secret keys authorize access to your project's data through the built-in `service_role` Postgres role. By design, this role has full access to your project's data. It also has the [`BYPASSRLS` attribute](https://www.postgresql.org/docs/current/ddl-rowsecurity.html#:~:text=BYPASSRLS), so it skips every Row Level Security policy you attach. -The secret key is an improvement over the old JWT-based `service_role` key, and we recommend using it where possible. It adds more checks to prevent misuse, specifically: +Secret keys improve on the old JWT-based `service_role` key, and we recommend them wherever possible. They add checks that prevent misuse: -- You cannot use a secret key in the browser (matches on the `User-Agent` header) and it will always reply with HTTP 401 Unauthorized. -- You don't need to have any secret keys if you are not using them. +- A secret key doesn't work in a browser. Supabase matches on the `User-Agent` header and returns HTTP 401 Unauthorized. +- A project doesn't need any secret keys if nothing uses them. ### Best practices for handling secret keys -Below are some starting guidelines on how to securely work with secret keys: +Follow these guidelines when you work with secret keys: - Always work with secret keys on computers you fully own or control. -- Use secure & encrypted send tools to share API keys with others (often provided by good password managers), but prefer the [**Settings > API Keys**](/dashboard/project/_/settings/api-keys/) section of the Dashboard instead. -- Prefer encrypting them when stored in files or environment variables. -- Do not add in source control, especially for CI scripts and tools. Prefer using the tool's native secrets capability instead. -- Prefer using a separate secret key for each separate backend component of your application, so that if one is found to be vulnerable or to have leaked the key you will only need to change it and not all. -- Even though a secret key will always return HTTP 401 Unauthorized error when used in a browser, it does not mean that attackers will not use it with other tools. Delete immediately! -- If you must include them in logs, log the first few random characters (but never more than 6). -- If you wish to log or store which valid API key was used, store it as a SHA256 hash. +- Share keys through an encrypted transfer tool, such as the one in your password manager. Better still, have the other person read the key from the [**Settings > API Keys**](/dashboard/project/_/settings/api-keys/) section of the Dashboard. +- Encrypt keys stored in files or environment variables. +- Keep keys out of source control, including CI scripts. Use the tool's own secrets storage instead. +- Use a separate secret key for each backend component. If one component leaks its key, you only rotate that one. +- Delete a leaked key immediately. The browser block returns HTTP 401 Unauthorized, but an attacker can still use the key from other tools. +- Log no more than 6 characters if you must record a key, counted from the random part after its prefix. +- Store a SHA256 hash if you need to record which key was used. ### What to do if a secret key or `service_role` has been leaked or compromised? -Don't rush if this has happened, or you are suspecting it has. Make sure you have fully considered the situation and have remediated the root cause of the suspicion or vulnerability **first**. Consider using the [OWASP Risk Rating Methodology](https://owasp.org/www-community/OWASP_Risk_Rating_Methodology) as an easy way to identify the severity of the incident and to plan your next steps. +Don't rush. Fix the root cause of the leak first, before you rotate anything. The [OWASP Risk Rating Methodology](https://owasp.org/www-community/OWASP_Risk_Rating_Methodology) helps you judge the severity of the incident and plan your next steps. -To rotate a secret key (`sb_secret_...`), use the [**Settings > API Keys**](/dashboard/project/_/settings/api-keys/) section of the Dashboard to create a new secret API key, then replace it with the compromised key. Once all components are using the new key, delete the compromised one. +To rotate a secret key, create a new one in the [**Settings > API Keys**](/dashboard/project/_/settings/api-keys/) section of the Dashboard, then replace the compromised key with the new one everywhere your application uses it. Once every component uses the new key, delete the compromised one. -**Deleting a secret key is irreversible and once done it will be gone forever.** +Deleting a secret key is irreversible. -If you are still using the JWT-based `service_role` key, replace the `service_role` key with a new secret key instead. Follow the guide from above as if you are rotating an existing secret key. +If you still use the JWT-based `service_role` key, replace it with a new secret key. Follow the same steps as for rotating a secret key. ## Known limitations and compatibility differences -As the publishable and secret keys are no longer JWT-based, there are some known limitations and compatibility differences that you may need to plan for: +Publishable and secret keys aren't JWTs, which creates a few compatibility differences to plan for: -- You cannot send a publishable or secret key in the `Authorization: Bearer ...` header, except if the value exactly equals the `apikey` header. In this case, your request will be forwarded down to your project's database, but will be rejected as the value is not a JWT. -- Edge Functions **only support JWT verification** via the `anon` and `service_role` JWT-based API keys. You will need to use the `--no-verify-jwt` option when using publishable and secret keys. The Supabase platform does not verify the `apikey` header when using Edge Functions in this way. Implement your own `apikey`-header authorization logic inside the Edge Function code itself. -- Public Realtime connections are limited to 24 hours in duration, unless the connection is upgraded and further maintained with user-level authentication via Supabase Auth or a supported Third-Party Auth provider. +- You can't send a publishable or secret key in the `Authorization: Bearer ...` header unless the value exactly matches the `apikey` header. Supabase forwards that request to your project's database, which rejects it because the value isn't a JWT. +- Edge Functions only support JWT verification through the `anon` and `service_role` JWT-based API keys. Use the `--no-verify-jwt` option with publishable and secret keys. The Supabase platform doesn't verify the `apikey` header for Edge Functions called this way, so implement your own `apikey` authorization inside the function. +- Public Realtime connections last a maximum of 24 hours, unless the connection is upgraded to user-level authentication through Supabase Auth or a supported third-party auth provider.