From 2c85a42c6f06e3e5711ff410c59b2c39cba6eea9 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 12 Nov 2025 12:50:17 -0800 Subject: [PATCH 01/46] API Keys docs --- api-keys-usage.md | 159 ++++++++++++++++++ .../development/machine-auth/api-keys.mdx | 147 ++++++++++++++++ .../development/machine-auth/overview.mdx | 6 +- .../guides/development/verifying-api-keys.mdx | 75 +++++++++ docs/manifest.json | 36 ++++ docs/reference/backend/api-keys/create.mdx | 95 +++++++++++ docs/reference/backend/api-keys/list.mdx | 77 +++++++++ docs/reference/backend/api-keys/revoke.mdx | 56 ++++++ .../backend/api-keys/verify-secret.mdx | 38 +++++ 9 files changed, 686 insertions(+), 3 deletions(-) create mode 100644 api-keys-usage.md create mode 100644 docs/guides/development/machine-auth/api-keys.mdx create mode 100644 docs/guides/development/verifying-api-keys.mdx create mode 100644 docs/reference/backend/api-keys/create.mdx create mode 100644 docs/reference/backend/api-keys/list.mdx create mode 100644 docs/reference/backend/api-keys/revoke.mdx create mode 100644 docs/reference/backend/api-keys/verify-secret.mdx diff --git a/api-keys-usage.md b/api-keys-usage.md new file mode 100644 index 0000000000..2dc0e094c7 --- /dev/null +++ b/api-keys-usage.md @@ -0,0 +1,159 @@ +API Keys backend SDK methods + +```ts +import type { ClerkPaginationRequest } from '@clerk/shared/types'; + +import type { PaginatedResourceResponse } from '../../api/resources/Deserializer'; +import { joinPaths } from '../../util/path'; +import type { APIKey } from '../resources/APIKey'; +import { AbstractAPI } from './AbstractApi'; + +const basePath = '/api_keys'; + +type GetAPIKeyListParams = ClerkPaginationRequest<{ + /** + * The user or organization ID to query API keys by + */ + subject: string; + /** + * Whether to include invalid API keys. + * + * @default false + */ + includeInvalid?: boolean; +}>; + +type CreateAPIKeyParams = { + /** + * API key name + */ + name: string; + /** + * The user or organization ID to associate the API key with + */ + subject: string; + /** + * API key description + */ + description?: string | null; + claims?: Record | null; + scopes?: string[]; + createdBy?: string | null; + secondsUntilExpiration?: number | null; +}; + +type RevokeAPIKeyParams = { + /** + * API key ID + */ + apiKeyId: string; + /** + * Reason for revocation + */ + revocationReason?: string | null; +}; + +export class APIKeysAPI extends AbstractAPI { + async list(queryParams: GetAPIKeyListParams) { + return this.request>({ + method: 'GET', + path: basePath, + queryParams, + }); + } + + async create(params: CreateAPIKeyParams) { + return this.request({ + method: 'POST', + path: basePath, + bodyParams: params, + }); + } + + async revoke(params: RevokeAPIKeyParams) { + const { apiKeyId, ...bodyParams } = params; + + this.requireId(apiKeyId); + + return this.request({ + method: 'POST', + path: joinPaths(basePath, apiKeyId, 'revoke'), + bodyParams, + }); + } + + async getSecret(apiKeyId: string) { + this.requireId(apiKeyId); + + return this.request<{ secret: string }>({ + method: 'GET', + path: joinPaths(basePath, apiKeyId, 'secret'), + }); + } + + async verifySecret(secret: string) { + return this.request({ + method: 'POST', + path: joinPaths(basePath, 'verify'), + bodyParams: { secret }, + }); + } +} +``` + +Verifying in Nextjs + +```ts +import { auth } from '@clerk/nextjs/server' + +export async function GET() { + // Needs to have the `acceptsToken: 'api_key'` for verifying api keys + const { userId } = await auth({ acceptsToken: 'api_key' }) + + // If userId returns null, the token is invalid + if (!userId) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + } + + return NextResponse.json({ userId }) +} +``` + +More context + +### Context + +Currently, Clerk only produces authentication tokens that are designed around signed-in users being granted access to application data - primarily the [session token](https://clerk.com/docs/how-clerk-works/overview#session-token), which is a fast-expiring JWT stored in a browser cookie. + +There are however several use cases for which Clerk customers need to be able to generate tokens that are passed to third party services which can call in to their application’s endpoints programmatically, from outside a browser environment, which Clerk’s existing token handling is not equipped for. The aim of this high level initiative is to enable these use cases. The known use cases are: + +- **API Keys**: Users of Clerk apps would like to be able to create API keys which grant 3rd party services access to app endpoints on the user’s behalf. +- **OAuth Tokens**: Users of Clerk apps would like to be able to grant 3rd party services access to app endpoints on the user’s behalf via OAuth2 consent flows. +- **M2M Tokens**: Developers building Clerk apps would like to be able to mint tokens using Clerk that allow (micro)services on the backend to be able to securely communicate between each other. + +For these use cases, we had to consider what type of token would be appropriate. The only type of token Clerk currently produces, our session token, is [designed as a JWT](https://clerk.com/docs/backend-requests/resources/tokens-and-signatures#json-web-tokens-jwts) for a number of reasons: + +- JWTs don’t require a network call on verification, which reduces latency for a flow that sits in the critical path of application performance (frontend ↔ backend communication). +- We have the means through our SDKs to implement JWTs with short expiration periods, which reduce security risks and give us the power to implement mechanisms for refreshing the tokens as need be. + +### Opaque Tokens + +Neither of these conditions are the case for machine tokens, however. In our research and discussions, we concluded that a better idea would be to use what is referred to as [“opaque tokens”](https://www.ory.sh/docs/oauth2-oidc/jwt-access-token), which are tokens that require a network call in order to verify, unlike JWTs, for all three use cases. Here’s the rationale for each use case: + +- **API Keys**: You do not want an API key that expires, generally. Expiring API tokens make it such that every time you enter an API key into a service, it becomes a time bomb in which after expiring, that service will break. Instead, it’s much more normal to create API keys without an expiration, and allow users to revoke or rotate their keys if there is any sort of security issue. Opaque tokens are perfect use case for this flow, as they are able to be somewhat safely created with long/indefinite expiration times since they can be instantly revoked, unlike JWTs. +- **OAuth Tokens**: While both opaque tokens and JWTs work fine for this use case, we are planning on defaulting to opaque tokens for the same reasons that Ory does this, [which they lay out very clearly in their docs](https://www.ory.sh/docs/oauth2-oidc/jwt-access-token#limitations). These tokens would still have an expiration however, unlike API keys, as this is [the expectation of the OAuth spec](https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.2). The plan would be to allow this to be configured to return a JWT for those who prefer it though. +- **M2M Tokens**: We plan to default to opaque tokens here largely for security reasons - when communicating between two backend services, it would be easy to assume that embedding secrets or sensitive values into the token’s claims would be safe. However, this would not be the case with JWTs, which have public claims, despite the fact that many who have not thoroughly researched them are under the impression that JWTs are “secure” or encrypted, which they are not. As microservice communications on the backend are less performance-sensitive than client-server communication within an app, we feel like the performance costs of the network call on verification is worth it, given the security benefits. The plan would be to allow this to be configured to return a JWT for those who desire the performance benefits of doing so. Requiring a manual change to switch this also gives us an opportunity to educate the users about the security risks that we would not have were it a default. + +More context + +### Context + +Most developers have used API keys throughout their careers. *API Keys* are typically long-lived tokens associated with a user or an organization that provide access to set of API endpoints for a third party on that user/organization’s behalf. + +An example would be a C1’s App wanting to provide some level of access to their app’s API to their C2s. For a more concrete example, consider ChatGPT who might vend API Keys to Developers on their platform that wish to pull their recent chats. In this case, the API key would be scoped to a specific user and only allow access to that subject’s chats. In other cases, an API key might have a subject of an Organization or even more granular scopes like “*can read chats but can’t write new chats to the API*”. + +### Proposal + +Clerk will support C1s ability to vend API Keys to their C2s. Clerk will also provide API endpoints that allow verification of the api key and it’s surrounding attributes, like claims. + +In addition to providing the ability to manage API Keys, Clerk will also provide UI components that C1s are able to drop into their applications to support C2’s in generating and managing API Keys. \ No newline at end of file diff --git a/docs/guides/development/machine-auth/api-keys.mdx b/docs/guides/development/machine-auth/api-keys.mdx new file mode 100644 index 0000000000..f3ba4c4c7f --- /dev/null +++ b/docs/guides/development/machine-auth/api-keys.mdx @@ -0,0 +1,147 @@ +--- +title: "Using API keys" +description: Learn how to use Clerk's API keys feature to allow your application's users to create API keys that delegate access to your application's API on their behalf. +--- + +Clerk's API keys feature allows your application's users to create API keys that can delegate access to your application's API on their behalf. API keys are typically long-lived tokens associated with a user or organization that provide access to a set of API endpoints for third-party services. + +For example, if you're building an application like ChatGPT, you might want to allow developers on your platform to create API keys that grant them access to pull their recent chats. In this case, the API key would be scoped to a specific user and only allow access to that user's chats. API keys can also be scoped to organizations or have granular scopes like "can read chats but can't write new chats to the API". + +This guide demonstrates how to create API keys, list them, use them to authenticate requests, and revoke them when needed. + +> [!NOTE] +> If you find that the use case being described does not fit what you are looking to accomplish with machine authentication, check out the [machine authentication overview](/docs/guides/development/machine-auth/overview) for more information on the different types of machine authentication that Clerk supports and what features are available for each type. + +## Creating API keys + +To create an API key, call the [`create()`](/docs/reference/backend/api-keys/create) method: + +```ts +const apiKey = await clerkClient.apiKeys.create({ + name: 'My API Key', + subject: 'user_xxx', // or 'org_xxx' for organization API keys + description: 'API key for accessing my application', + scopes: ['read:users', 'write:users'], // optional + secondsUntilExpiration: 86400, // optional: expires in 24 hours +}) +``` + +The method will return an object with the following properties, including the `secret`: + +```json +{ + "id": "ak_xxx", + "name": "My API Key", + "description": "API key for accessing my application", + "subject": "user_xxx", + "scopes": ["read:users", "write:users"], + "claims": null, + "revoked": false, + "revocationReason": null, + "expired": false, + "expiration": 1754942036732, + "createdAt": 1754938436732, + "updatedAt": 1754938436732, + "createdBy": "user_xxx", + "secret": "ak_live_xxx" +} +``` + +> [!WARNING] +> The API key secret is only available in the response from `create()` and cannot be retrieved again. Make sure to store the secret securely immediately after creation. If you lose the secret, you will need to create a new API key. + +### Required parameters + +- `name` - A descriptive name for the API key (e.g., "Production API Key", "Development Key") +- `subject` - The user ID (`user_xxx`) or organization ID (`org_xxx`) to associate the API key with + +### Optional parameters + +- `description` - A longer description of what the API key is used for +- `scopes` - An array of scope strings that define what the API key can access +- `claims` - A JavaScript object that can be used to store additional information about the API key +- `createdBy` - The user ID of the user creating the API key (for audit purposes) +- `secondsUntilExpiration` - The number of seconds until the API key will expire. By default, the API key will not expire. API keys are typically long-lived tokens that don't expire, as expiring API keys would cause third-party services using them to break unexpectedly. Instead, API keys can be instantly revoked if there's a security issue, which is possible because they use opaque tokens rather than JWTs. + +## Listing API keys + +To list API keys, call the [`list()`](/docs/reference/backend/api-keys/list) method: + +```ts +const apiKeys = await clerkClient.apiKeys.list({ + subject: 'user_xxx', // Filter by user or organization ID + includeInvalid: false, // Whether to include revoked or expired keys +}) +``` + +The method returns a paginated response with API key objects (without secrets): + +```json +{ + "data": [ + { + "id": "ak_xxx", + "name": "My API Key", + "description": "API key for accessing my application", + "subject": "user_xxx", + "scopes": ["read:users"], + "revoked": false, + "expired": false, + "expiration": 1754942036732, + "createdAt": 1754938436732, + "updatedAt": 1754938436732 + } + ], + "totalCount": 1 +} +``` + +## Using API keys in requests + +Once you have an API key secret, you can use it to authenticate requests to your application's API. The API key should be sent as a Bearer token in the `Authorization` header: + +```ts +await fetch('https://your-api.com/users', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${apiKeySecret}`, + }, +}) +``` + +On your backend, you can verify the API key using Clerk's SDK. See the [verifying API keys guide](/docs/guides/development/verifying-api-keys) for framework-specific examples. + +## Verifying API keys + +To verify an API key secret, you can use the [`verifySecret()`](/docs/reference/backend/api-keys/verify-secret) method: + +```ts +const apiKey = await clerkClient.apiKeys.verifySecret(secret) +``` + +If the API key is valid, the method will return the API key object with its properties. If the API key is invalid, revoked, or expired, the method will throw an error. + +> [!NOTE] +> In most cases, you'll want to verify API keys using framework-specific helpers like `auth()` in Next.js, which handles the verification automatically. See the [verifying API keys guide](/docs/guides/development/verifying-api-keys) for more details. + +## Revoking API keys + +To revoke an API key, call the [`revoke()`](/docs/reference/backend/api-keys/revoke) method: + +```ts +await clerkClient.apiKeys.revoke({ + apiKeyId: apiKey.id, + revocationReason: 'Key compromised', // optional, for your records +}) +``` + +This will revoke the API key and prevent it from being used to authenticate any future requests. The API key will remain in your list but will be marked as revoked. + +> [!IMPORTANT] +> When you revoke an API key, it is immediately invalidated. Any requests using that API key will be rejected. Make sure to notify users or update your systems before revoking API keys that are in active use. + +## UI Components + +Clerk provides UI components that you can drop into your application to support your users in generating and managing API keys. You can integrate these components into your application's user interface, or create your own custom UI for API key management using the backend SDK methods described in this guide. + diff --git a/docs/guides/development/machine-auth/overview.mdx b/docs/guides/development/machine-auth/overview.mdx index 027f10d02a..dd2ef44194 100644 --- a/docs/guides/development/machine-auth/overview.mdx +++ b/docs/guides/development/machine-auth/overview.mdx @@ -3,11 +3,11 @@ title: Machine Authentication description: Learn what machine authentication is, why it's used, and how it works with Clerk. --- -Machine authentication (often referred to as "machine-to-machine", or "M2M" authentication) is a way to authenticate machines, such as servers, applications, and devices, to ensure that they are who they say they are. This is in contrast to user authentication, which verifies the identity of a human user. Clerk currently supports two types of machine authentication tokens, with plans to add a third in the near future: +Machine authentication (often referred to as "machine-to-machine", or "M2M" authentication) is a way to authenticate machines, such as servers, applications, and devices, to ensure that they are who they say they are. This is in contrast to user authentication, which verifies the identity of a human user. Clerk currently supports three types of machine authentication tokens: - [OAuth access tokens](#o-auth-access-tokens) - [M2M (machine-to-machine) tokens](#m2-m-tokens) -- [API keys](#api-keys) (not yet supported - [get notified when it's available](https://feedback.clerk.com/roadmap?id=beee0250-bfd3-4207-9865-2bebd1c49078)) +- [API keys](#api-keys) ## OAuth access tokens @@ -27,4 +27,4 @@ To learn more about machine authentication with M2M tokens, see the [M2M tokens] If you'd like for your application's users to be able to create API keys that can delegate access to your application's API on their behalf, this is the intended use case for Clerk's API keys feature. With this feature, you will be able to allow users to create API keys directly through [the `` component](/docs/reference/components/user/user-profile), or create your own UI for API keys via custom hooks. It will also be simple to verify API keys on your backend using Clerk's SDKs, and you or your users will be able to instantly revoke API keys at any time. -We haven't released API key support yet, but we're working hard on it and hoping to have it available soon. If you're interested in early access, or being notified when it's available, please [add your feedback here](https://feedback.clerk.com/roadmap?id=beee0250-bfd3-4207-9865-2bebd1c49078). +Clerk's API keys feature is now available. To learn more about machine authentication with API keys, see the [API keys](/docs/guides/development/machine-auth/api-keys) guide. diff --git a/docs/guides/development/verifying-api-keys.mdx b/docs/guides/development/verifying-api-keys.mdx new file mode 100644 index 0000000000..4edd3ed962 --- /dev/null +++ b/docs/guides/development/verifying-api-keys.mdx @@ -0,0 +1,75 @@ +--- +title: Verify API keys in your Next.js application with Clerk +description: Learn how to use Clerk's helpers to verify API keys in your Next.js application. +sdk: nextjs +--- + +When building a resource server that needs to accept and verify API keys issued by Clerk, it's crucial to verify these keys on your backend to ensure the request is coming from an authenticated client. + +Clerk's Next.js SDK provides a built-in [`auth()`](/docs/reference/nextjs/app-router/auth) function that supports token validation via the `acceptsToken` parameter. This lets you specify which type(s) of token your API route should accept. You can also use the [`auth.protect()`](/docs/reference/nextjs/app-router/auth#auth-protect) method to check if a request includes a valid machine token (e.g. API key or OAuth token) and enforce access rules accordingly. + +By default, `acceptsToken` is set to `session_token`, which means API keys will **not** be accepted unless explicitly configured. You can pass either a **single token type** or an **array of token types** to `acceptsToken`. To learn more about the supported token types, see the [`auth()` parameters documentation](/docs/reference/nextjs/app-router/auth#parameters). + +Below are two examples of verifying API keys in a Next.js API route using Clerk's SDK: + +## Example 1: Accepting a single token type + +In the following example, the `acceptsToken` parameter is set to only accept `api_key`s. + +- If the API key is invalid or missing, `auth()` will return `null` for `userId` and other properties, and the request will be rejected with a `401` response. +- If the API key is valid, `userId` is returned and the token claims are available for use in the application logic. + +```tsx {{ filename: 'app/api/example/route.ts' }} +import { NextResponse } from 'next/server' +import { auth } from '@clerk/nextjs/server' + +export async function GET() { + const { isAuthenticated, claims, userId } = await auth({ acceptsToken: 'api_key' }) + + // If auth() returns null, the API key is invalid + if (!isAuthenticated) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + } + + return NextResponse.json({ userId, claims }) +} +``` + +## Example 2: Accepting multiple token types + +In the following example, the `acceptsToken` option allows `session_token`s, `oauth_token`s, and `api_key`s. + +- If the token is invalid or missing, `auth()` will return `false` for `isAuthenticated` and `null` for other properties, like `userId`. +- If the token is an `api_key`, the code can access the associated user's data using the `userId`. +- If the token is valid, `isAuthenticated` is `true` and `userId` is returned and available for use in the application logic. This example includes pseudo-code that uses the `userId` to get data from a database. + +```tsx {{ filename: 'app/api/example/route.ts' }} +import { NextRequest, NextResponse } from 'next/server' +import { auth } from '@clerk/nextjs/server' + +export async function POST(req: NextRequest) { + // Accept session_token, oauth_token, and api_key types + const { isAuthenticated, tokenType, userId, scopes } = await auth({ + acceptsToken: ['session_token', 'oauth_token', 'api_key'], + }) + + // If auth() returns null, the token is invalid + if (!isAuthenticated) { + return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) + } + + // Check if the token is an oauth_token and if it doesn't have the required scope + if (tokenType === 'oauth_token' && !scopes?.includes('profile')) { + return NextResponse.json({ error: 'OAuth token missing the "profile" scope' }, { status: 401 }) + } + + // If the token is valid, move forward with the application logic + // This example includes pseudo-code for getting data from a database using the userId + const data = db.select().from(user).where(eq(user.id, userId)) + + return NextResponse.json({ data }) +} +``` + +You can also protect entire route groups using [`clerkMiddleware()`](/docs/reference/nextjs/clerk-middleware). See how to implement this in [the middleware docs](/docs/reference/nextjs/clerk-middleware#protect-routes-based-on-token-types). + diff --git a/docs/manifest.json b/docs/manifest.json index d7f54f00e5..fa544ee396 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -635,6 +635,10 @@ { "title": "Using M2M tokens", "href": "/docs/guides/development/machine-auth/m2m-tokens" + }, + { + "title": "Using API keys", + "href": "/docs/guides/development/machine-auth/api-keys" } ] ], @@ -815,6 +819,10 @@ "title": "Verifying OAuth access tokens", "href": "/docs/guides/development/verifying-oauth-access-tokens" }, + { + "title": "Verifying API keys", + "href": "/docs/guides/development/verifying-api-keys" + }, { "title": "SPA Mode", "href": "/docs/guides/development/spa-mode" @@ -2738,6 +2746,34 @@ ] ] }, + { + "title": "API Keys", + "collapse": true, + "items": [ + [ + { + "title": "`create()`", + "wrap": false, + "href": "/docs/reference/backend/api-keys/create" + }, + { + "title": "`list()`", + "wrap": false, + "href": "/docs/reference/backend/api-keys/list" + }, + { + "title": "`revoke()`", + "wrap": false, + "href": "/docs/reference/backend/api-keys/revoke" + }, + { + "title": "`verifySecret()`", + "wrap": false, + "href": "/docs/reference/backend/api-keys/verify-secret" + } + ] + ] + }, { "title": "OAuth applications", "collapse": true, diff --git a/docs/reference/backend/api-keys/create.mdx b/docs/reference/backend/api-keys/create.mdx new file mode 100644 index 0000000000..c5db3fd48d --- /dev/null +++ b/docs/reference/backend/api-keys/create.mdx @@ -0,0 +1,95 @@ +--- +title: '`create()`' +description: Use Clerk's Backend SDK to create an API key. +sdk: js-backend +--- + +Creates a new [API key](/docs/guides/development/machine-auth/api-keys). + +```ts +function create(params: CreateAPIKeyParams): Promise +``` + +## `CreateAPIKeyParams` + + + - `name` + - `string` + + A descriptive name for the API key (e.g., "Production API Key", "Development Key"). + + --- + + - `subject` + - `string` + + The user ID (`user_xxx`) or organization ID (`org_xxx`) to associate the API key with. + + --- + + - `description?` + - `string | null` + + A longer description of what the API key is used for. + + --- + + - `scopes?` + - `string[]` + + An array of scope strings that define what the API key can access. + + --- + + - `claims?` + - `Record | null` + + A JavaScript object that can be used to store additional information about the API key. + + --- + + - `createdBy?` + - `string | null` + + The user ID of the user creating the API key (for audit purposes). + + --- + + - `secondsUntilExpiration?` + - `number | null` + + The number of seconds until the API key will expire. By default, the API key will not expire. API keys are typically long-lived tokens that don't expire, as expiring API keys would cause third-party services using them to break unexpectedly. + + +## Example + + + +### Basic API key creation + +```ts +const apiKey = await clerkClient.apiKeys.create({ + name: 'My API Key', + subject: 'user_xxx', +}) +``` + +### API key with optional parameters + +```ts +const apiKey = await clerkClient.apiKeys.create({ + name: 'Production API Key', + subject: 'user_xxx', + description: 'API key for accessing my application', + scopes: ['read:users', 'write:users'], + secondsUntilExpiration: 86400, // expires in 24 hours +}) +``` + +> [!WARNING] +> The API key secret is only available in the response from `create()` and cannot be retrieved again. Make sure to store the secret securely immediately after creation. + +## Backend API (BAPI) endpoint + +This method in the SDK is a wrapper around the BAPI endpoint `POST/api_keys`. See the [BAPI reference](/docs/reference/backend-api/tag/api-keys/post/api_keys){{ target: '_blank' }} for more information. + diff --git a/docs/reference/backend/api-keys/list.mdx b/docs/reference/backend/api-keys/list.mdx new file mode 100644 index 0000000000..bd46885ff4 --- /dev/null +++ b/docs/reference/backend/api-keys/list.mdx @@ -0,0 +1,77 @@ +--- +title: '`list()`' +description: Use Clerk's Backend SDK to list API keys. +sdk: js-backend +--- + +Lists API keys for a given user or organization. + +```ts +function list(queryParams: GetAPIKeyListParams): Promise> +``` + +## `GetAPIKeyListParams` + + + - `subject` + - `string` + + The user or organization ID to query API keys by. + + --- + + - `includeInvalid?` + - `boolean` + + Whether to include invalid API keys (revoked or expired). Defaults to `false`. + + --- + + - `limit?` + - `number` + + The maximum number of API keys to return. Defaults to `10`. + + --- + + - `offset?` + - `number` + + The number of API keys to skip before returning results. Defaults to `0`. + + +## Example + + + +### List API keys for a user + +```ts +const apiKeys = await clerkClient.apiKeys.list({ + subject: 'user_xxx', +}) +``` + +### List API keys including invalid ones + +```ts +const apiKeys = await clerkClient.apiKeys.list({ + subject: 'user_xxx', + includeInvalid: true, +}) +``` + +### List API keys with pagination + +```ts +const apiKeys = await clerkClient.apiKeys.list({ + subject: 'user_xxx', + limit: 20, + offset: 0, +}) +``` + +## Backend API (BAPI) endpoint + +This method in the SDK is a wrapper around the BAPI endpoint `GET/api_keys`. See the [BAPI reference](/docs/reference/backend-api/tag/api-keys/get/api_keys){{ target: '_blank' }} for more information. + diff --git a/docs/reference/backend/api-keys/revoke.mdx b/docs/reference/backend/api-keys/revoke.mdx new file mode 100644 index 0000000000..221a8c155f --- /dev/null +++ b/docs/reference/backend/api-keys/revoke.mdx @@ -0,0 +1,56 @@ +--- +title: '`revoke()`' +description: Use Clerk's Backend SDK to revoke an API key. +sdk: js-backend +--- + +Revokes an [API key](/docs/guides/development/machine-auth/api-keys). This will immediately invalidate the API key and prevent it from being used to authenticate any future requests. + +```ts +function revoke(params: RevokeAPIKeyParams): Promise +``` + +## `RevokeAPIKeyParams` + + + - `apiKeyId` + - `string` + + The ID of the API key to revoke. + + --- + + - `revocationReason?` + - `string | null` + + Optional reason for revocation. Useful for your records. + + +## Example + + + +### Revoke an API key + +```ts +const apiKey = await clerkClient.apiKeys.revoke({ + apiKeyId: 'ak_xxx', +}) +``` + +### Revoke an API key with a reason + +```ts +const apiKey = await clerkClient.apiKeys.revoke({ + apiKeyId: 'ak_xxx', + revocationReason: 'Key compromised', +}) +``` + +> [!IMPORTANT] +> When you revoke an API key, it is immediately invalidated. Any requests using that API key will be rejected. Make sure to notify users or update your systems before revoking API keys that are in active use. + +## Backend API (BAPI) endpoint + +This method in the SDK is a wrapper around the BAPI endpoint `POST/api_keys/{apiKeyID}/revoke`. See the [BAPI reference](/docs/reference/backend-api/tag/api-keys/post/api_keys/%7BapiKeyID%7D/revoke){{ target: '_blank' }} for more information. + diff --git a/docs/reference/backend/api-keys/verify-secret.mdx b/docs/reference/backend/api-keys/verify-secret.mdx new file mode 100644 index 0000000000..b16b32939e --- /dev/null +++ b/docs/reference/backend/api-keys/verify-secret.mdx @@ -0,0 +1,38 @@ +--- +title: '`verifySecret()`' +description: Use Clerk's Backend SDK to verify an API key secret. +sdk: js-backend +--- + +Verifies an [API key](/docs/guides/development/machine-auth/api-keys) secret. If the API key is valid, the method returns the API key object with its properties. If the API key is invalid, revoked, or expired, the method will throw an error. + +> [!NOTE] +> In most cases, you'll want to verify API keys using framework-specific helpers like `auth()` in Next.js, which handles the verification automatically. See the [verifying API keys guide](/docs/guides/development/verifying-api-keys) for more details. + +```ts +function verifySecret(secret: string): Promise +``` + +## Parameters + + + - `secret` + - `string` + + The API key secret to verify. + + +## Example + + + +### Verify an API key secret + +```ts +const apiKey = await clerkClient.apiKeys.verifySecret('ak_live_xxx') +``` + +## Backend API (BAPI) endpoint + +This method in the SDK is a wrapper around the BAPI endpoint `POST/api_keys/verify`. See the [BAPI reference](/docs/reference/backend-api/tag/api-keys/post/api_keys/verify){{ target: '_blank' }} for more information. + From 98565182e992dc420d75a430d1300d71c5333748 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 12 Nov 2025 12:51:04 -0800 Subject: [PATCH 02/46] remove unused file --- api-keys-usage.md | 159 ---------------------------------------------- 1 file changed, 159 deletions(-) delete mode 100644 api-keys-usage.md diff --git a/api-keys-usage.md b/api-keys-usage.md deleted file mode 100644 index 2dc0e094c7..0000000000 --- a/api-keys-usage.md +++ /dev/null @@ -1,159 +0,0 @@ -API Keys backend SDK methods - -```ts -import type { ClerkPaginationRequest } from '@clerk/shared/types'; - -import type { PaginatedResourceResponse } from '../../api/resources/Deserializer'; -import { joinPaths } from '../../util/path'; -import type { APIKey } from '../resources/APIKey'; -import { AbstractAPI } from './AbstractApi'; - -const basePath = '/api_keys'; - -type GetAPIKeyListParams = ClerkPaginationRequest<{ - /** - * The user or organization ID to query API keys by - */ - subject: string; - /** - * Whether to include invalid API keys. - * - * @default false - */ - includeInvalid?: boolean; -}>; - -type CreateAPIKeyParams = { - /** - * API key name - */ - name: string; - /** - * The user or organization ID to associate the API key with - */ - subject: string; - /** - * API key description - */ - description?: string | null; - claims?: Record | null; - scopes?: string[]; - createdBy?: string | null; - secondsUntilExpiration?: number | null; -}; - -type RevokeAPIKeyParams = { - /** - * API key ID - */ - apiKeyId: string; - /** - * Reason for revocation - */ - revocationReason?: string | null; -}; - -export class APIKeysAPI extends AbstractAPI { - async list(queryParams: GetAPIKeyListParams) { - return this.request>({ - method: 'GET', - path: basePath, - queryParams, - }); - } - - async create(params: CreateAPIKeyParams) { - return this.request({ - method: 'POST', - path: basePath, - bodyParams: params, - }); - } - - async revoke(params: RevokeAPIKeyParams) { - const { apiKeyId, ...bodyParams } = params; - - this.requireId(apiKeyId); - - return this.request({ - method: 'POST', - path: joinPaths(basePath, apiKeyId, 'revoke'), - bodyParams, - }); - } - - async getSecret(apiKeyId: string) { - this.requireId(apiKeyId); - - return this.request<{ secret: string }>({ - method: 'GET', - path: joinPaths(basePath, apiKeyId, 'secret'), - }); - } - - async verifySecret(secret: string) { - return this.request({ - method: 'POST', - path: joinPaths(basePath, 'verify'), - bodyParams: { secret }, - }); - } -} -``` - -Verifying in Nextjs - -```ts -import { auth } from '@clerk/nextjs/server' - -export async function GET() { - // Needs to have the `acceptsToken: 'api_key'` for verifying api keys - const { userId } = await auth({ acceptsToken: 'api_key' }) - - // If userId returns null, the token is invalid - if (!userId) { - return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) - } - - return NextResponse.json({ userId }) -} -``` - -More context - -### Context - -Currently, Clerk only produces authentication tokens that are designed around signed-in users being granted access to application data - primarily the [session token](https://clerk.com/docs/how-clerk-works/overview#session-token), which is a fast-expiring JWT stored in a browser cookie. - -There are however several use cases for which Clerk customers need to be able to generate tokens that are passed to third party services which can call in to their application’s endpoints programmatically, from outside a browser environment, which Clerk’s existing token handling is not equipped for. The aim of this high level initiative is to enable these use cases. The known use cases are: - -- **API Keys**: Users of Clerk apps would like to be able to create API keys which grant 3rd party services access to app endpoints on the user’s behalf. -- **OAuth Tokens**: Users of Clerk apps would like to be able to grant 3rd party services access to app endpoints on the user’s behalf via OAuth2 consent flows. -- **M2M Tokens**: Developers building Clerk apps would like to be able to mint tokens using Clerk that allow (micro)services on the backend to be able to securely communicate between each other. - -For these use cases, we had to consider what type of token would be appropriate. The only type of token Clerk currently produces, our session token, is [designed as a JWT](https://clerk.com/docs/backend-requests/resources/tokens-and-signatures#json-web-tokens-jwts) for a number of reasons: - -- JWTs don’t require a network call on verification, which reduces latency for a flow that sits in the critical path of application performance (frontend ↔ backend communication). -- We have the means through our SDKs to implement JWTs with short expiration periods, which reduce security risks and give us the power to implement mechanisms for refreshing the tokens as need be. - -### Opaque Tokens - -Neither of these conditions are the case for machine tokens, however. In our research and discussions, we concluded that a better idea would be to use what is referred to as [“opaque tokens”](https://www.ory.sh/docs/oauth2-oidc/jwt-access-token), which are tokens that require a network call in order to verify, unlike JWTs, for all three use cases. Here’s the rationale for each use case: - -- **API Keys**: You do not want an API key that expires, generally. Expiring API tokens make it such that every time you enter an API key into a service, it becomes a time bomb in which after expiring, that service will break. Instead, it’s much more normal to create API keys without an expiration, and allow users to revoke or rotate their keys if there is any sort of security issue. Opaque tokens are perfect use case for this flow, as they are able to be somewhat safely created with long/indefinite expiration times since they can be instantly revoked, unlike JWTs. -- **OAuth Tokens**: While both opaque tokens and JWTs work fine for this use case, we are planning on defaulting to opaque tokens for the same reasons that Ory does this, [which they lay out very clearly in their docs](https://www.ory.sh/docs/oauth2-oidc/jwt-access-token#limitations). These tokens would still have an expiration however, unlike API keys, as this is [the expectation of the OAuth spec](https://datatracker.ietf.org/doc/html/rfc6749#section-4.2.2). The plan would be to allow this to be configured to return a JWT for those who prefer it though. -- **M2M Tokens**: We plan to default to opaque tokens here largely for security reasons - when communicating between two backend services, it would be easy to assume that embedding secrets or sensitive values into the token’s claims would be safe. However, this would not be the case with JWTs, which have public claims, despite the fact that many who have not thoroughly researched them are under the impression that JWTs are “secure” or encrypted, which they are not. As microservice communications on the backend are less performance-sensitive than client-server communication within an app, we feel like the performance costs of the network call on verification is worth it, given the security benefits. The plan would be to allow this to be configured to return a JWT for those who desire the performance benefits of doing so. Requiring a manual change to switch this also gives us an opportunity to educate the users about the security risks that we would not have were it a default. - -More context - -### Context - -Most developers have used API keys throughout their careers. *API Keys* are typically long-lived tokens associated with a user or an organization that provide access to set of API endpoints for a third party on that user/organization’s behalf. - -An example would be a C1’s App wanting to provide some level of access to their app’s API to their C2s. For a more concrete example, consider ChatGPT who might vend API Keys to Developers on their platform that wish to pull their recent chats. In this case, the API key would be scoped to a specific user and only allow access to that subject’s chats. In other cases, an API key might have a subject of an Organization or even more granular scopes like “*can read chats but can’t write new chats to the API*”. - -### Proposal - -Clerk will support C1s ability to vend API Keys to their C2s. Clerk will also provide API endpoints that allow verification of the api key and it’s surrounding attributes, like claims. - -In addition to providing the ability to manage API Keys, Clerk will also provide UI components that C1s are able to drop into their applications to support C2’s in generating and managing API Keys. \ No newline at end of file From 3646448dd87c131bedea0a961677b5ca86a44bc2 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Wed, 12 Nov 2025 12:54:50 -0800 Subject: [PATCH 03/46] run format --- docs/guides/development/machine-auth/api-keys.mdx | 1 - docs/guides/development/verifying-api-keys.mdx | 1 - docs/reference/backend/api-keys/create.mdx | 1 - docs/reference/backend/api-keys/list.mdx | 1 - docs/reference/backend/api-keys/revoke.mdx | 1 - docs/reference/backend/api-keys/verify-secret.mdx | 1 - 6 files changed, 6 deletions(-) diff --git a/docs/guides/development/machine-auth/api-keys.mdx b/docs/guides/development/machine-auth/api-keys.mdx index f3ba4c4c7f..edf0624d0e 100644 --- a/docs/guides/development/machine-auth/api-keys.mdx +++ b/docs/guides/development/machine-auth/api-keys.mdx @@ -144,4 +144,3 @@ This will revoke the API key and prevent it from being used to authenticate any ## UI Components Clerk provides UI components that you can drop into your application to support your users in generating and managing API keys. You can integrate these components into your application's user interface, or create your own custom UI for API key management using the backend SDK methods described in this guide. - diff --git a/docs/guides/development/verifying-api-keys.mdx b/docs/guides/development/verifying-api-keys.mdx index 4edd3ed962..2cc80d3689 100644 --- a/docs/guides/development/verifying-api-keys.mdx +++ b/docs/guides/development/verifying-api-keys.mdx @@ -72,4 +72,3 @@ export async function POST(req: NextRequest) { ``` You can also protect entire route groups using [`clerkMiddleware()`](/docs/reference/nextjs/clerk-middleware). See how to implement this in [the middleware docs](/docs/reference/nextjs/clerk-middleware#protect-routes-based-on-token-types). - diff --git a/docs/reference/backend/api-keys/create.mdx b/docs/reference/backend/api-keys/create.mdx index c5db3fd48d..d856b54435 100644 --- a/docs/reference/backend/api-keys/create.mdx +++ b/docs/reference/backend/api-keys/create.mdx @@ -92,4 +92,3 @@ const apiKey = await clerkClient.apiKeys.create({ ## Backend API (BAPI) endpoint This method in the SDK is a wrapper around the BAPI endpoint `POST/api_keys`. See the [BAPI reference](/docs/reference/backend-api/tag/api-keys/post/api_keys){{ target: '_blank' }} for more information. - diff --git a/docs/reference/backend/api-keys/list.mdx b/docs/reference/backend/api-keys/list.mdx index bd46885ff4..fd69942e91 100644 --- a/docs/reference/backend/api-keys/list.mdx +++ b/docs/reference/backend/api-keys/list.mdx @@ -74,4 +74,3 @@ const apiKeys = await clerkClient.apiKeys.list({ ## Backend API (BAPI) endpoint This method in the SDK is a wrapper around the BAPI endpoint `GET/api_keys`. See the [BAPI reference](/docs/reference/backend-api/tag/api-keys/get/api_keys){{ target: '_blank' }} for more information. - diff --git a/docs/reference/backend/api-keys/revoke.mdx b/docs/reference/backend/api-keys/revoke.mdx index 221a8c155f..27b53aa404 100644 --- a/docs/reference/backend/api-keys/revoke.mdx +++ b/docs/reference/backend/api-keys/revoke.mdx @@ -53,4 +53,3 @@ const apiKey = await clerkClient.apiKeys.revoke({ ## Backend API (BAPI) endpoint This method in the SDK is a wrapper around the BAPI endpoint `POST/api_keys/{apiKeyID}/revoke`. See the [BAPI reference](/docs/reference/backend-api/tag/api-keys/post/api_keys/%7BapiKeyID%7D/revoke){{ target: '_blank' }} for more information. - diff --git a/docs/reference/backend/api-keys/verify-secret.mdx b/docs/reference/backend/api-keys/verify-secret.mdx index b16b32939e..46e9fa2bef 100644 --- a/docs/reference/backend/api-keys/verify-secret.mdx +++ b/docs/reference/backend/api-keys/verify-secret.mdx @@ -35,4 +35,3 @@ const apiKey = await clerkClient.apiKeys.verifySecret('ak_live_xxx') ## Backend API (BAPI) endpoint This method in the SDK is a wrapper around the BAPI endpoint `POST/api_keys/verify`. See the [BAPI reference](/docs/reference/backend-api/tag/api-keys/post/api_keys/verify){{ target: '_blank' }} for more information. - From a1b872bcadbf0106cf91f9f59719f37620467848 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 17 Nov 2025 14:56:30 -0800 Subject: [PATCH 04/46] add APIKeys AIO reference --- docs/manifest.json | 13 ++ docs/reference/components/api-keys.mdx | 197 +++++++++++++++++++++++++ 2 files changed, 210 insertions(+) create mode 100644 docs/reference/components/api-keys.mdx diff --git a/docs/manifest.json b/docs/manifest.json index fa544ee396..91448fc619 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -3239,6 +3239,19 @@ ] ] }, + { + "title": "API Keys Components", + "collapse": false, + "items": [ + [ + { + "title": "``", + "wrap": false, + "href": "/docs/reference/components/api-keys" + } + ] + ] + }, { "title": "Control components", "collapse": false, diff --git a/docs/reference/components/api-keys.mdx b/docs/reference/components/api-keys.mdx new file mode 100644 index 0000000000..ea7964cdd1 --- /dev/null +++ b/docs/reference/components/api-keys.mdx @@ -0,0 +1,197 @@ +--- +title: '`` component' +description: Clerk's component for managing API keys. +--- + +The `` component is used to manage API keys for your application. It allows you to create, edit, and revoke API keys for your application. + +The component manages API keys based on the user's current context. When the user has an active organization selected, all operations are scoped to that organization. Otherwise, operations are user-scoped. + +To utilize the `` component, you must first enable API keys in the Clerk Dashboard. + +> [!WARNING] +> The `` component is currently in early preview. To use this component, you need to have your instance flagged for API keys. Contact Clerk support to enable this feature for your instance. + +## Properties + +All props are optional. + + + - `perPage` + - `number` + + The number of API keys to show per page. Defaults to 10. + + --- + + - `appearance` + - [Appearance](/docs/customization/overview) | undefined + + Optional object to style your components. Will only affect [Clerk components](/docs/components/overview) and not [Account Portal](/docs/account-portal/overview) pages. + + --- + + - `fallback?` + - `ReactNode` + + An optional element to be rendered while the component is mounting. + + +## Usage with frameworks + +The following example includes a basic implementation of the `` component. You can use this as a starting point for your own implementation. + + + + ```tsx {{ filename: 'app/api-keys/page.tsx' }} + import { APIKeys } from '@clerk/nextjs' + + export default function Page() { + return + } + ``` + + + + ```tsx {{ filename: 'src/api-keys.tsx' }} + import { APIKeys } from '@clerk/clerk-react' + + export default function Page() { + return + } + ``` + + + + ```tsx {{ filename: 'app/routes/api-keys.tsx' }} + import { APIKeys } from '@clerk/react-router' + + export default function Page() { + return + } + ``` + + + + ```tsx {{ filename: 'src/routes/api-keys.tsx' }} + import { APIKeys } from '@clerk/tanstack-react-start' + import { createFileRoute } from '@tanstack/react-router' + + export const Route = createFileRoute('/api-keys')({ + component: Page, + }) + + function Page() { + return + } + ``` + + + + ```vue {{ filename: 'api-keys.vue' }} + + + + ``` + + + + + ## Usage with JavaScript + + The following methods available on an instance of the [`Clerk`](/docs/references/javascript/clerk) class are used to render and control the `` component: + + - [`mountApiKeys()`](#mount-api-keys) + - [`unmountApiKeys()`](#unmount-api-keys) + + The following examples assume that you followed the [quickstart](/docs/quickstarts/javascript) to add Clerk to your JavaScript app. + + ### `mountApiKeys()` + + ```typescript + function mountApiKeys(node: HTMLDivElement, props?: APIKeysProps): void + ``` + + #### `mountAPIKeys()` params + + + - `node` + - [`HTMLDivElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) + + The container `
` element used to render in the `` component + + --- + + - `props?` + - [`APIKeysProps`](#properties) + + The properties to pass to the `` component + + + #### `mountAPIKeys()` usage + + ```js {{ filename: 'main.js', mark: [15] }} + import { Clerk } from '@clerk/clerk-js' + + // Initialize Clerk with your Clerk Publishable Key + const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY + + const clerk = new Clerk(clerkPubKey) + await clerk.load() + + document.getElementById('app').innerHTML = ` +
+ ` + + const apiKeysDiv = document.getElementById('api-keys') + + clerk.mountAPIKeys(apiKeysDiv) + ``` + + ### `unmountAPIKeys()` + + ```typescript + function unmountApiKeys(node: HTMLDivElement): void + ``` + + #### `unmountAPIKeys()` params + + + - `node` + - [`HTMLDivElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLDivElement) + + The container `
` element with a rendered `` component instance + + + #### `unmountAPIKeys()` usage + + ```js {{ filename: 'main.js', mark: [19] }} + import { Clerk } from '@clerk/clerk-js' + + // Initialize Clerk with your Clerk Publishable Key + const clerkPubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY + + const clerk = new Clerk(clerkPubKey) + await clerk.load() + + document.getElementById('app').innerHTML = ` +
+ ` + + const apiKeysDiv = document.getElementById('api-keys') + + clerk.mountAPIKeys(apiKeysDiv) + + // ... + + clerk.unmountAPIKeys(apiKeysDiv) + ``` + + +## Customization + +To learn about how to customize Clerk components, see the [customization guide](/docs/customization/overview). From a6d649336b18cf9102f1edc0e8bd213e8b9184d3 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 2 Dec 2025 19:27:18 -0800 Subject: [PATCH 05/46] rename verify method --- docs/guides/development/machine-auth/api-keys.mdx | 4 ++-- docs/manifest.json | 2 +- .../backend/api-keys/{verify-secret.mdx => verify.mdx} | 6 +++--- 3 files changed, 6 insertions(+), 6 deletions(-) rename docs/reference/backend/api-keys/{verify-secret.mdx => verify.mdx} (87%) diff --git a/docs/guides/development/machine-auth/api-keys.mdx b/docs/guides/development/machine-auth/api-keys.mdx index edf0624d0e..a80c03bc1d 100644 --- a/docs/guides/development/machine-auth/api-keys.mdx +++ b/docs/guides/development/machine-auth/api-keys.mdx @@ -114,10 +114,10 @@ On your backend, you can verify the API key using Clerk's SDK. See the [verifyin ## Verifying API keys -To verify an API key secret, you can use the [`verifySecret()`](/docs/reference/backend/api-keys/verify-secret) method: +To verify an API key secret, you can use the [`verify()`](/docs/reference/backend/api-keys/verify) method: ```ts -const apiKey = await clerkClient.apiKeys.verifySecret(secret) +const apiKey = await clerkClient.apiKeys.verify(secret) ``` If the API key is valid, the method will return the API key object with its properties. If the API key is invalid, revoked, or expired, the method will throw an error. diff --git a/docs/manifest.json b/docs/manifest.json index 91448fc619..a3125d9a5c 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -2767,7 +2767,7 @@ "href": "/docs/reference/backend/api-keys/revoke" }, { - "title": "`verifySecret()`", + "title": "`verify()`", "wrap": false, "href": "/docs/reference/backend/api-keys/verify-secret" } diff --git a/docs/reference/backend/api-keys/verify-secret.mdx b/docs/reference/backend/api-keys/verify.mdx similarity index 87% rename from docs/reference/backend/api-keys/verify-secret.mdx rename to docs/reference/backend/api-keys/verify.mdx index 46e9fa2bef..ae66da428d 100644 --- a/docs/reference/backend/api-keys/verify-secret.mdx +++ b/docs/reference/backend/api-keys/verify.mdx @@ -1,5 +1,5 @@ --- -title: '`verifySecret()`' +title: '`verify()`' description: Use Clerk's Backend SDK to verify an API key secret. sdk: js-backend --- @@ -10,7 +10,7 @@ Verifies an [API key](/docs/guides/development/machine-auth/api-keys) secret. If > In most cases, you'll want to verify API keys using framework-specific helpers like `auth()` in Next.js, which handles the verification automatically. See the [verifying API keys guide](/docs/guides/development/verifying-api-keys) for more details. ```ts -function verifySecret(secret: string): Promise +function verify(secret: string): Promise ``` ## Parameters @@ -29,7 +29,7 @@ function verifySecret(secret: string): Promise ### Verify an API key secret ```ts -const apiKey = await clerkClient.apiKeys.verifySecret('ak_live_xxx') +const apiKey = await clerkClient.apiKeys.verify('ak_live_xxx') ``` ## Backend API (BAPI) endpoint From 3a98e48eef3d5e10e9844463f74ae2a195ffd570 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Tue, 2 Dec 2025 22:10:46 -0800 Subject: [PATCH 06/46] fix incorrect method --- docs/manifest.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/manifest.json b/docs/manifest.json index a3125d9a5c..29cdd82c4c 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -2769,7 +2769,7 @@ { "title": "`verify()`", "wrap": false, - "href": "/docs/reference/backend/api-keys/verify-secret" + "href": "/docs/reference/backend/api-keys/verify" } ] ] From 0ec9ccd19b83dbb364699faf6ec28f210b083cfc Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Thu, 4 Dec 2025 17:40:18 -0500 Subject: [PATCH 07/46] polish round --- .../development/machine-auth/api-keys.mdx | 107 +++++++++++++----- .../guides/development/verifying-api-keys.mdx | 13 +-- docs/reference/components/api-keys.mdx | 31 +++-- 3 files changed, 102 insertions(+), 49 deletions(-) diff --git a/docs/guides/development/machine-auth/api-keys.mdx b/docs/guides/development/machine-auth/api-keys.mdx index a80c03bc1d..71db01f72c 100644 --- a/docs/guides/development/machine-auth/api-keys.mdx +++ b/docs/guides/development/machine-auth/api-keys.mdx @@ -5,14 +5,65 @@ description: Learn how to use Clerk's API keys feature to allow your application Clerk's API keys feature allows your application's users to create API keys that can delegate access to your application's API on their behalf. API keys are typically long-lived tokens associated with a user or organization that provide access to a set of API endpoints for third-party services. -For example, if you're building an application like ChatGPT, you might want to allow developers on your platform to create API keys that grant them access to pull their recent chats. In this case, the API key would be scoped to a specific user and only allow access to that user's chats. API keys can also be scoped to organizations or have granular scopes like "can read chats but can't write new chats to the API". +For example, if you're building an application like ChatGPT, you might want to allow developers on your platform to create API keys that grant them access to pull their recent chats. In this case, the API key would be scoped to a specific user and only allow access to that user's chats. API keys can also be scoped to organizations or have granular scopes like "can read chats but can't write new chats". This guide demonstrates how to create API keys, list them, use them to authenticate requests, and revoke them when needed. > [!NOTE] > If you find that the use case being described does not fit what you are looking to accomplish with machine authentication, check out the [machine authentication overview](/docs/guides/development/machine-auth/overview) for more information on the different types of machine authentication that Clerk supports and what features are available for each type. -## Creating API keys +## Prerequisites + +Before you can use API keys in your application, you must enable them in the Clerk Dashboard: + +1. In the Clerk Dashboard, navigate to [**Configure > API keys**](https://dashboard.clerk.com/~/platform/api-keys). +1. Enable API keys for users, organizations, or both, depending on your use case. + +> [!NOTE] +> If the API keys feature is disabled in the Dashboard, any existing keys will fail verification but will not be revoked. If you re-enable the feature, those keys will work again. This is different from [revoking individual keys](#revoking-api-keys), which permanently invalidates them. + +## Enabling your users to create API keys via Clerk's UI components + +Clerk provides UI components that you can drop into your application to support your users in generating and managing API keys. You can integrate these components into your application's interface, or create your own custom logic for API key management using the [backend SDK methods described below](#using-api-keys-via-clerk-s-sdks). + +If you are using Clerk's [``](/docs/reference/components/user/user-profile) component, or [``](/docs/reference/components/organization/organization-profile) component, and you have enabled API keys in the Clerk Dashboard, you will automatically see an **API Keys** tab appear in the user or organization profile components. This tab will allow your users to create, list, and revoke API keys, without writing any additional custom code. + +If you would like to prevent the API Keys tab from appearing in the user or organization profile components, you can pass a prop to the component to hide it as such: + +```tsx +// UserProfile Component + + +// OrganizationProfile Component + + +// UserButton Component + +``` + +It's also possible to render the [`` component](/docs/reference/components/api-keys) directly if you'd like to embed the API key management UI directly into your application's user interface. + +## Using API keys in requests + +Once you have an API key, you can use it to authenticate requests to your application's API. The API key should be sent as a Bearer token in the `Authorization` header: + +```ts +await fetch('https://your-api.com/users', { + method: 'GET', + headers: { + 'Content-Type': 'application/json', + Authorization: `Bearer ${apiKey}`, + }, +}) +``` + +On your backend, you can verify the API key using Clerk's SDKs. See the [verifying API keys guide](/docs/guides/development/verifying-api-keys) for framework-specific examples. + +## Using API keys via Clerk's SDKs + +If you would like to create, list, and revoke API keys programmatically on the server side, you can use Clerk's backend SDKs. In this guide, we will use the [JavaScript Backend SDK](/docs/js-backend/getting-started/quickstart) to demonstrate usage, but please note that the API keys feature is available in all of Clerk's backend SDKs. + +### Creating API keys To create an API key, call the [`create()`](/docs/reference/backend/api-keys/create) method: @@ -20,13 +71,13 @@ To create an API key, call the [`create()`](/docs/reference/backend/api-keys/cre const apiKey = await clerkClient.apiKeys.create({ name: 'My API Key', subject: 'user_xxx', // or 'org_xxx' for organization API keys - description: 'API key for accessing my application', + description: 'API key for accessing my application', // optional scopes: ['read:users', 'write:users'], // optional secondsUntilExpiration: 86400, // optional: expires in 24 hours }) ``` -The method will return an object with the following properties, including the `secret`: +The method returns an object containing the API key details, including the `secret` property with the actual API key value: ```json { @@ -43,19 +94,19 @@ The method will return an object with the following properties, including the `s "createdAt": 1754938436732, "updatedAt": 1754938436732, "createdBy": "user_xxx", - "secret": "ak_live_xxx" + "secret": "ak_xxx" } ``` > [!WARNING] -> The API key secret is only available in the response from `create()` and cannot be retrieved again. Make sure to store the secret securely immediately after creation. If you lose the secret, you will need to create a new API key. +> The API key secret is only available in the initial response from `create()` and cannot be retrieved again. Make sure to store the secret securely immediately after creation. If you lose the secret, you will need to create a new API key. -### Required parameters +#### Required parameters - `name` - A descriptive name for the API key (e.g., "Production API Key", "Development Key") - `subject` - The user ID (`user_xxx`) or organization ID (`org_xxx`) to associate the API key with -### Optional parameters +#### Optional parameters - `description` - A longer description of what the API key is used for - `scopes` - An array of scope strings that define what the API key can access @@ -63,7 +114,7 @@ The method will return an object with the following properties, including the `s - `createdBy` - The user ID of the user creating the API key (for audit purposes) - `secondsUntilExpiration` - The number of seconds until the API key will expire. By default, the API key will not expire. API keys are typically long-lived tokens that don't expire, as expiring API keys would cause third-party services using them to break unexpectedly. Instead, API keys can be instantly revoked if there's a security issue, which is possible because they use opaque tokens rather than JWTs. -## Listing API keys +### Listing API keys To list API keys, call the [`list()`](/docs/reference/backend/api-keys/list) method: @@ -96,36 +147,36 @@ The method returns a paginated response with API key objects (without secrets): } ``` -## Using API keys in requests +### Verifying API keys -Once you have an API key secret, you can use it to authenticate requests to your application's API. The API key should be sent as a Bearer token in the `Authorization` header: +To verify an API key secret, you can use the [`verify()`](/docs/reference/backend/api-keys/verify) method: ```ts -await fetch('https://your-api.com/users', { - method: 'GET', - headers: { - 'Content-Type': 'application/json', - Authorization: `Bearer ${apiKeySecret}`, - }, -}) +const apiKey = await clerkClient.apiKeys.verify(secret) ``` -On your backend, you can verify the API key using Clerk's SDK. See the [verifying API keys guide](/docs/guides/development/verifying-api-keys) for framework-specific examples. - -## Verifying API keys +If the API key is valid, the method will return the API key object with its properties. If the API key is invalid, revoked, or expired, the method will throw an error. -To verify an API key secret, you can use the [`verify()`](/docs/reference/backend/api-keys/verify) method: +The following example demonstrates how to handle verification errors: ```ts -const apiKey = await clerkClient.apiKeys.verify(secret) +import { isClerkAPIResponseError } from '@clerk/shared/error' + +try { + const apiKey = await clerkClient.apiKeys.verify(secret) + // API key is valid, proceed with the request +} catch (error) { + if (isClerkAPIResponseError(error)) { + console.error(error.errors[0]?.message) + } + throw error +} ``` -If the API key is valid, the method will return the API key object with its properties. If the API key is invalid, revoked, or expired, the method will throw an error. - > [!NOTE] > In most cases, you'll want to verify API keys using framework-specific helpers like `auth()` in Next.js, which handles the verification automatically. See the [verifying API keys guide](/docs/guides/development/verifying-api-keys) for more details. -## Revoking API keys +### Revoking API keys To revoke an API key, call the [`revoke()`](/docs/reference/backend/api-keys/revoke) method: @@ -140,7 +191,3 @@ This will revoke the API key and prevent it from being used to authenticate any > [!IMPORTANT] > When you revoke an API key, it is immediately invalidated. Any requests using that API key will be rejected. Make sure to notify users or update your systems before revoking API keys that are in active use. - -## UI Components - -Clerk provides UI components that you can drop into your application to support your users in generating and managing API keys. You can integrate these components into your application's user interface, or create your own custom UI for API key management using the backend SDK methods described in this guide. diff --git a/docs/guides/development/verifying-api-keys.mdx b/docs/guides/development/verifying-api-keys.mdx index 2cc80d3689..175ae29c90 100644 --- a/docs/guides/development/verifying-api-keys.mdx +++ b/docs/guides/development/verifying-api-keys.mdx @@ -4,7 +4,7 @@ description: Learn how to use Clerk's helpers to verify API keys in your Next.js sdk: nextjs --- -When building a resource server that needs to accept and verify API keys issued by Clerk, it's crucial to verify these keys on your backend to ensure the request is coming from an authenticated client. +When building a server that needs to accept and verify API keys issued by Clerk, it's crucial to verify these keys on your backend to ensure the request is coming from an authenticated client. Clerk's Next.js SDK provides a built-in [`auth()`](/docs/reference/nextjs/app-router/auth) function that supports token validation via the `acceptsToken` parameter. This lets you specify which type(s) of token your API route should accept. You can also use the [`auth.protect()`](/docs/reference/nextjs/app-router/auth#auth-protect) method to check if a request includes a valid machine token (e.g. API key or OAuth token) and enforce access rules accordingly. @@ -17,7 +17,7 @@ Below are two examples of verifying API keys in a Next.js API route using Clerk' In the following example, the `acceptsToken` parameter is set to only accept `api_key`s. - If the API key is invalid or missing, `auth()` will return `null` for `userId` and other properties, and the request will be rejected with a `401` response. -- If the API key is valid, `userId` is returned and the token claims are available for use in the application logic. +- If the API key is valid, `userId` and `claims` are returned. ```tsx {{ filename: 'app/api/example/route.ts' }} import { NextResponse } from 'next/server' @@ -26,7 +26,6 @@ import { auth } from '@clerk/nextjs/server' export async function GET() { const { isAuthenticated, claims, userId } = await auth({ acceptsToken: 'api_key' }) - // If auth() returns null, the API key is invalid if (!isAuthenticated) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } @@ -53,14 +52,14 @@ export async function POST(req: NextRequest) { acceptsToken: ['session_token', 'oauth_token', 'api_key'], }) - // If auth() returns null, the token is invalid + // If the token is invalid or missing if (!isAuthenticated) { return NextResponse.json({ error: 'Unauthorized' }, { status: 401 }) } - // Check if the token is an oauth_token and if it doesn't have the required scope - if (tokenType === 'oauth_token' && !scopes?.includes('profile')) { - return NextResponse.json({ error: 'OAuth token missing the "profile" scope' }, { status: 401 }) + // Check if the token is an api key and if it doesn't have the required scope + if (tokenType === 'api_key' && !scopes?.includes('write:users')) { + return NextResponse.json({ error: 'API Key missing the "write:users" scope' }, { status: 401 }) } // If the token is valid, move forward with the application logic diff --git a/docs/reference/components/api-keys.mdx b/docs/reference/components/api-keys.mdx index ea7964cdd1..31b1b50454 100644 --- a/docs/reference/components/api-keys.mdx +++ b/docs/reference/components/api-keys.mdx @@ -9,22 +9,26 @@ The component manages API keys based on the user's current context. When the use To utilize the `` component, you must first enable API keys in the Clerk Dashboard. -> [!WARNING] -> The `` component is currently in early preview. To use this component, you need to have your instance flagged for API keys. Contact Clerk support to enable this feature for your instance. - ## Properties All props are optional. - - `perPage` + - `perPage?` - `number` - The number of API keys to show per page. Defaults to 10. + The number of API keys to show per page. Defaults to `10`. + + --- + + - `showDescription?` + - `boolean` + + Whether to show the description field in the API key creation form. Defaults to `false`. --- - - `appearance` + - `appearance?` - [Appearance](/docs/customization/overview) | undefined Optional object to style your components. Will only affect [Clerk components](/docs/components/overview) and not [Account Portal](/docs/account-portal/overview) pages. @@ -105,15 +109,15 @@ The following example includes a basic implementation of the `` compo The following methods available on an instance of the [`Clerk`](/docs/references/javascript/clerk) class are used to render and control the `` component: - - [`mountApiKeys()`](#mount-api-keys) - - [`unmountApiKeys()`](#unmount-api-keys) + - [`mountAPIKeys()`](#mount-api-keys) + - [`unmountAPIKeys()`](#unmount-api-keys) The following examples assume that you followed the [quickstart](/docs/quickstarts/javascript) to add Clerk to your JavaScript app. - ### `mountApiKeys()` + ### `mountAPIKeys()` ```typescript - function mountApiKeys(node: HTMLDivElement, props?: APIKeysProps): void + function mountAPIKeys(node: HTMLDivElement, props?: APIKeysProps): void ``` #### `mountAPIKeys()` params @@ -149,13 +153,16 @@ The following example includes a basic implementation of the `` compo const apiKeysDiv = document.getElementById('api-keys') - clerk.mountAPIKeys(apiKeysDiv) + clerk.mountAPIKeys(apiKeysDiv, { + perPage: 10, + showDescription: true, + }) ``` ### `unmountAPIKeys()` ```typescript - function unmountApiKeys(node: HTMLDivElement): void + function unmountAPIKeys(node: HTMLDivElement): void ``` #### `unmountAPIKeys()` params From 09be5982c3e1dbc2c7d4ceae61194923a0bee9cf Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Thu, 4 Dec 2025 17:47:21 -0500 Subject: [PATCH 08/46] add a little security detail --- docs/guides/development/machine-auth/api-keys.mdx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/guides/development/machine-auth/api-keys.mdx b/docs/guides/development/machine-auth/api-keys.mdx index 71db01f72c..1eafcc4e72 100644 --- a/docs/guides/development/machine-auth/api-keys.mdx +++ b/docs/guides/development/machine-auth/api-keys.mdx @@ -43,6 +43,9 @@ If you would like to prevent the API Keys tab from appearing in the user or orga It's also possible to render the [`` component](/docs/reference/components/api-keys) directly if you'd like to embed the API key management UI directly into your application's user interface. +> [!TIP] +> Hiding the UI doesn't prevent users from creating API keys — they can still call Clerk's Frontend API directly. If you want to ensure only backend-issued API keys are valid, use the `scopes` or `claims` parameters (available only via the [Backend SDK](#creating-api-keys)) when creating keys. Your verification logic can then reject any keys missing the required scopes or claims, making user-created keys ineffective. + ## Using API keys in requests Once you have an API key, you can use it to authenticate requests to your application's API. The API key should be sent as a Bearer token in the `Authorization` header: From 379aa9cd30b6f5c3d0520e4a65d3553eb739f824 Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Thu, 4 Dec 2025 17:48:44 -0500 Subject: [PATCH 09/46] fix lint error --- docs/guides/development/machine-auth/api-keys.mdx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/guides/development/machine-auth/api-keys.mdx b/docs/guides/development/machine-auth/api-keys.mdx index 1eafcc4e72..652a60059d 100644 --- a/docs/guides/development/machine-auth/api-keys.mdx +++ b/docs/guides/development/machine-auth/api-keys.mdx @@ -31,13 +31,14 @@ If you are using Clerk's [``](/docs/reference/components/user/use If you would like to prevent the API Keys tab from appearing in the user or organization profile components, you can pass a prop to the component to hide it as such: ```tsx -// UserProfile Component +``` -// OrganizationProfile Component +```tsx +``` -// UserButton Component +```tsx ``` From e5b048fb4118caa5b7bec6e5e5c84409db0a942f Mon Sep 17 00:00:00 2001 From: Jeff Escalante Date: Thu, 4 Dec 2025 17:55:28 -0500 Subject: [PATCH 10/46] fix some links --- docs/guides/development/machine-auth/api-keys.mdx | 2 +- docs/reference/components/api-keys.mdx | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/guides/development/machine-auth/api-keys.mdx b/docs/guides/development/machine-auth/api-keys.mdx index 652a60059d..2f3357ef95 100644 --- a/docs/guides/development/machine-auth/api-keys.mdx +++ b/docs/guides/development/machine-auth/api-keys.mdx @@ -24,7 +24,7 @@ Before you can use API keys in your application, you must enable them in the Cle ## Enabling your users to create API keys via Clerk's UI components -Clerk provides UI components that you can drop into your application to support your users in generating and managing API keys. You can integrate these components into your application's interface, or create your own custom logic for API key management using the [backend SDK methods described below](#using-api-keys-via-clerk-s-sdks). +Clerk provides UI components that you can drop into your application to support your users in generating and managing API keys. You can integrate these components into your application's interface, or create your own custom logic for API key management using the [backend SDK methods described below](#using-api-keys-via-clerks-sdks). If you are using Clerk's [``](/docs/reference/components/user/user-profile) component, or [``](/docs/reference/components/organization/organization-profile) component, and you have enabled API keys in the Clerk Dashboard, you will automatically see an **API Keys** tab appear in the user or organization profile components. This tab will allow your users to create, list, and revoke API keys, without writing any additional custom code. diff --git a/docs/reference/components/api-keys.mdx b/docs/reference/components/api-keys.mdx index 31b1b50454..d8b6d25c6d 100644 --- a/docs/reference/components/api-keys.mdx +++ b/docs/reference/components/api-keys.mdx @@ -29,9 +29,9 @@ All props are optional. --- - `appearance?` - - [Appearance](/docs/customization/overview) | undefined + - [Appearance](/docs/guides/customizing-clerk/appearance-prop/overview) | undefined - Optional object to style your components. Will only affect [Clerk components](/docs/components/overview) and not [Account Portal](/docs/account-portal/overview) pages. + Optional object to style your components. Will only affect [Clerk components](/docs/reference/components/overview) and not [Account Portal](/docs/guides/customizing-clerk/account-portal) pages. --- @@ -107,12 +107,12 @@ The following example includes a basic implementation of the `` compo ## Usage with JavaScript - The following methods available on an instance of the [`Clerk`](/docs/references/javascript/clerk) class are used to render and control the `` component: + The following methods available on an instance of the [`Clerk`](/docs/reference/javascript/clerk) class are used to render and control the `` component: - [`mountAPIKeys()`](#mount-api-keys) - [`unmountAPIKeys()`](#unmount-api-keys) - The following examples assume that you followed the [quickstart](/docs/quickstarts/javascript) to add Clerk to your JavaScript app. + The following examples assume that you followed the [quickstart](/docs/js-frontend/getting-started/quickstart) to add Clerk to your JavaScript app. ### `mountAPIKeys()` @@ -201,4 +201,4 @@ The following example includes a basic implementation of the `` compo ## Customization -To learn about how to customize Clerk components, see the [customization guide](/docs/customization/overview). +To learn about how to customize Clerk components, see the [customization documentation](/docs/guides/customizing-clerk/appearance-prop/overview). From ef2c456f02d3c32d258ec99ad66af12cd7a228e2 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 8 Dec 2025 10:51:48 -0800 Subject: [PATCH 11/46] chore: add custom flows for API keys --- .../custom-flows/api-keys/manage-api-keys.mdx | 336 ++++++++++++++++++ docs/manifest.json | 20 ++ docs/reference/javascript/api-keys.mdx | 148 ++++++++ docs/reference/javascript/types/api-key.mdx | 122 +++++++ 4 files changed, 626 insertions(+) create mode 100644 docs/guides/development/custom-flows/api-keys/manage-api-keys.mdx create mode 100644 docs/reference/javascript/api-keys.mdx create mode 100644 docs/reference/javascript/types/api-key.mdx diff --git a/docs/guides/development/custom-flows/api-keys/manage-api-keys.mdx b/docs/guides/development/custom-flows/api-keys/manage-api-keys.mdx new file mode 100644 index 0000000000..020395283a --- /dev/null +++ b/docs/guides/development/custom-flows/api-keys/manage-api-keys.mdx @@ -0,0 +1,336 @@ +--- +title: Build a custom flow for managing API keys +description: Learn how to use the Clerk API to build a custom flow for creating, listing, and revoking API keys. +--- + + + +This guide will demonstrate how to use the Clerk API to build a custom flow for managing [API keys](/docs/guides/development/machine-auth/api-keys). API keys allow your application's users to create keys that grant third-party services access to your application's API endpoints on their behalf. + + + + The following example: + + 1. Uses the [`useAPIKeys()`](/docs/reference/hooks/use-api-keys) hook from `@clerk/nextjs/experimental` to retrieve and manage API keys. It will use `subject` if provided; otherwise it falls back to the active Organization, then the current user. + 2. Displays the API keys in a table with options to create new keys and revoke existing ones. + 3. Provides a form to create new API keys using [`clerk.apiKeys.create()`](/docs/reference/javascript/api-keys#create) with an optional expiration (subject defaults to active org, then user). + 4. Allows revoking API keys using [`clerk.apiKeys.revoke()`](/docs/reference/javascript/api-keys#revoke). + + This example is written for Next.js App Router but can be adapted for any React-based framework. + + ```jsx {{ filename: 'app/components/ApiKeysManager.tsx', collapsible: true }} + 'use client' + + import { useClerk } from '@clerk/nextjs' + import { useAPIKeys } from '@clerk/nextjs/experimental' + import React, { useMemo, useState } from 'react' + + export default function ApiKeysManager() { + const clerk = useClerk() + const [showCreateForm, setShowCreateForm] = useState(false) + const [formData, setFormData] = useState({ + name: '', + expirationSeconds: '', + }) + + const { data: apiKeys, isLoading, revalidate } = useAPIKeys() + + const handleCreate = async (e) => { + e.preventDefault() + + try { + const expiration = + formData.expirationSeconds.trim() === '' + ? null + : Number(formData.expirationSeconds) + + const newApiKey = await clerk.apiKeys.create({ + name: formData.name, + secondsUntilExpiration: expiration, + }) + + // Store the secret immediately - it won't be available again + alert(`API key created! Secret: ${newApiKey.secret}\n\nMake sure to save this secret - it won't be shown again.`) + + setFormData({ name: '', expirationSeconds: '' }) + setShowCreateForm(false) + revalidate() + } catch (error) { + console.error('Error creating API key:', error) + alert('Failed to create API key') + } + } + + const handleRevoke = async (apiKeyId) => { + if (!confirm('Are you sure you want to revoke this API key?')) { + return + } + + try { + await clerk.apiKeys.revoke({ + apiKeyId, + revocationReason: 'Revoked by user', + }) + revalidate() + } catch (error) { + console.error('Error revoking API key:', error) + alert('Failed to revoke API key') + } + } + + if (isLoading) { + return
Loading API keys...
+ } + + return ( +
+

API Keys

+ + + {showCreateForm && ( +
+
+ +
+
+ +
+ +
+ )} + + + + + + + + + + + {!apiKeys || apiKeys.length === 0 ? ( + + + + ) : ( + apiKeys.map((apiKey) => ( + + + + + + )) + )} + +
NameExpirationActions
No API keys found
{apiKey.name} + {apiKey.expiration + ? new Date(apiKey.expiration).toLocaleDateString() + : 'No expiration'} + + {!apiKey.revoked && !apiKey.expired && ( + + )} +
+
+ ) + } + ``` +
+ + + The following example: + + 1. Calls [`clerk.apiKeys.getAll()`](/docs/reference/javascript/api-keys#getall) to retrieve the list of API keys for the active user or organization. + 2. Displays the API keys in a table. + 3. Provides a form to create new API keys using [`clerk.apiKeys.create()`](/docs/reference/javascript/api-keys#create). + 4. Allows revoking API keys using [`clerk.apiKeys.revoke()`](/docs/reference/javascript/api-keys#revoke). + + Use the following tabs to view the code necessary for the `index.html` and `main.js` files. + + + ```html {{ filename: 'index.html', collapsible: true }} + + + + + + Clerk + JavaScript App - API Keys + + +
+ +

API Keys

+ + + + + + + + + + + + + +
NameExpirationActions
+ + + + + ``` +
+ + ```js {{ filename: 'main.js', collapsible: true }} + import { Clerk } from '@clerk/clerk-js' + + const pubKey = import.meta.env.VITE_CLERK_PUBLISHABLE_KEY + + if (!pubKey) { + throw new Error('Add your VITE_CLERK_PUBLISHABLE_KEY to .env file') + } + + const clerk = new Clerk(pubKey) + await clerk.load() + + if (clerk.isSignedIn) { + const user = clerk.user + await loadApiKeys(user.id) + + // Create form handlers + document.getElementById('create-btn').addEventListener('click', () => { + document.getElementById('create-form').style.display = 'block' + }) + + document.getElementById('cancel-btn').addEventListener('click', () => { + document.getElementById('create-form').style.display = 'none' + document.getElementById('api-key-form').reset() + }) + + document.getElementById('api-key-form').addEventListener('submit', async (e) => { + e.preventDefault() + + const name = document.getElementById('name-input').value + const expirationSeconds = document.getElementById('expiration-input').value + const secondsUntilExpiration = + expirationSeconds.trim() === '' ? null : Number(expirationSeconds) + + try { + const newApiKey = await clerk.apiKeys.create({ + name, + secondsUntilExpiration, + }) + + // Store the secret immediately - it won't be available again + alert(`API key created! Secret: ${newApiKey.secret}\n\nMake sure to save this secret - it won't be shown again.`) + + document.getElementById('api-key-form').reset() + document.getElementById('create-form').style.display = 'none' + await loadApiKeys(user.id) + } catch (error) { + console.error('Error creating API key:', error) + alert('Failed to create API key') + } + }) + } else { + // If there is no active user, mount Clerk's + document.getElementById('app').innerHTML = ` +
+ ` + + const signInDiv = document.getElementById('sign-in') + clerk.mountSignIn(signInDiv) + } + + async function loadApiKeys(userId) { + try { + const response = await clerk.apiKeys.getAll() + + const tableBody = document.getElementById('api-keys-table-body') + tableBody.innerHTML = '' + + if (response.data.length === 0) { + const row = tableBody.insertRow() + const cell = row.insertCell() + cell.colSpan = 3 + cell.textContent = 'No API keys found' + return + } + + response.data.forEach((apiKey) => { + const row = tableBody.insertRow() + row.insertCell().textContent = apiKey.name + + const expirationCell = row.insertCell() + expirationCell.textContent = apiKey.expiration + ? new Date(apiKey.expiration).toLocaleDateString() + : 'No expiration' + + const actionsCell = row.insertCell() + const revokeBtn = document.createElement('button') + revokeBtn.textContent = 'Revoke' + revokeBtn.addEventListener('click', async () => { + if (confirm('Are you sure you want to revoke this API key?')) { + try { + await clerk.apiKeys.revoke({ + apiKeyId: apiKey.id, + revocationReason: 'Revoked by user', + }) + await loadApiKeys(userId) + } catch (error) { + console.error('Error revoking API key:', error) + alert('Failed to revoke API key') + } + } + }) + actionsCell.appendChild(revokeBtn) + }) + } catch (error) { + console.error('Error loading API keys:', error) + } + } + ``` +
+
+ diff --git a/docs/manifest.json b/docs/manifest.json index 29cdd82c4c..eacaaf52be 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1158,6 +1158,18 @@ } ] ] + }, + { + "title": "API keys", + "collapse": false, + "items": [ + [ + { + "title": "Manage API keys", + "href": "/docs/guides/development/custom-flows/api-keys/manage-api-keys" + } + ] + ] } ] ], @@ -1680,6 +1692,10 @@ "title": "`Organization`", "href": "/docs/reference/javascript/organization" }, + { + "title": "`APIKeys`", + "href": "/docs/reference/javascript/api-keys" + }, { "title": "Types", "items": [ @@ -1752,6 +1768,10 @@ "title": "`BillingSubscriptionItemResource`", "href": "/docs/reference/javascript/types/billing-subscription-item-resource" }, + { + "title": "`APIKeyResource`", + "href": "/docs/reference/javascript/types/api-key" + }, { "title": "`CustomMenuItem`", "href": "/docs/reference/javascript/types/custom-menu-item" diff --git a/docs/reference/javascript/api-keys.mdx b/docs/reference/javascript/api-keys.mdx new file mode 100644 index 0000000000..55f2a835e5 --- /dev/null +++ b/docs/reference/javascript/api-keys.mdx @@ -0,0 +1,148 @@ +--- +title: '`APIKeys` namespace' +description: The APIKeys namespace provides methods for creating, listing, and revoking API keys that allow users to grant third-party services programmatic access to your application's API endpoints. +sdk: js-frontend +--- + +The `APIKeys` namespace provides methods for managing API keys that allow your application's users to grant third-party services programmatic access to your application's API endpoints on their behalf. API keys are long-lived, opaque tokens that can be instantly revoked. + +To use these methods, you must access them through the `Clerk` object: `clerk.apiKeys`. + +> [!NOTE] +> If a `subject` parameter is not provided, the methods will automatically use the active Organization ID if available, otherwise they will use the current User ID. + +## Methods + +### `getAll()` + +Retrieves a paginated list of API keys for the current user or organization. Returns a [`ClerkPaginatedResponse`][pag-ref] of [`APIKeyResource`](/docs/reference/javascript/types/api-key) objects. + +```typescript +function getAll(params?: GetAPIKeysParams): Promise> +``` + +#### `GetAPIKeysParams` + + + - `subject?` + - `string` + + The user or organization ID to query API keys by. If not provided, defaults to the active Organization, then the current User. + + --- + + - `query?` + - `string` + + A search query to filter API keys by name. + + --- + + - `initialPage?` + - `number` + + A number that can be used to skip the first n-1 pages. For example, if `initialPage` is set to 10, it will skip the first 9 pages and will fetch the 10th page. + + --- + + - `pageSize?` + - `number` + + A number that indicates the maximum number of results that should be returned for a specific page. + + +#### Example + +```js +await clerk.apiKeys.getAll() +``` + +### `create()` + +Creates a new API key. Returns an [`APIKeyResource`](/docs/reference/javascript/types/api-key) object that includes the `secret` property. **The secret is only available in the response from `create()` and cannot be retrieved later.** + +> [!WARNING] +> Make sure to store the API key secret immediately after creation, as it will not be available again. + +```typescript +function create(params: CreateAPIKeyParams): Promise +``` + +#### `CreateAPIKeyParams` + + + - `name` + - `string` + + The name of the API key. + + --- + + - `subject?` + - `string` + + The user or organization ID to associate the API key with. If not provided, defaults to the active Organization, then the current User. + + --- + + - `secondsUntilExpiration?` + - `number | null` + + The number of seconds until the API key expires. Set to `null` or omit to create a key that never expires. + + --- + + - `description?` + - `string | null` + + An optional description for the API key. + + +#### Example + +```js +const apiKey = await clerk.apiKeys.create({ + name: 'My API Key', + secondsUntilExpiration: 86400, // 24 hours + description: 'API key for third-party service', +}) + +// Store the secret immediately +console.log('API Key Secret:', apiKey.secret) +``` + +### `revoke()` + +Revokes an API key by ID. Returns an [`APIKeyResource`](/docs/reference/javascript/types/api-key) object. + +```typescript +function revoke(params: RevokeAPIKeyParams): Promise +``` + +#### `RevokeAPIKeyParams` + + + - `apiKeyID` + - `string` + + The ID of the API key to revoke. + + --- + + - `revocationReason?` + - `string | null` + + An optional reason for revoking the API key. + + +#### Example + +```js +await clerk.apiKeys.revoke({ + apiKeyID: 'ak_xxx', + revocationReason: 'No longer needed', +}) +``` + +[pag-ref]: /docs/reference/javascript/types/clerk-paginated-response + diff --git a/docs/reference/javascript/types/api-key.mdx b/docs/reference/javascript/types/api-key.mdx new file mode 100644 index 0000000000..145259318b --- /dev/null +++ b/docs/reference/javascript/types/api-key.mdx @@ -0,0 +1,122 @@ +--- +title: '`APIKeyResource`' +description: An interface that describes an API key resource that can be used to authenticate requests to your application's API endpoints. +sdk: js-frontend +--- + +An interface that describes an API key resource that can be used to authenticate requests to your application's API endpoints on behalf of a user or organization. + +## Properties + + + - `id` + - `string` + + The unique identifier of the API key. + + --- + + - `type` + - `string` + + The type of the API key. Currently always `'api_key'`. + + --- + + - `name` + - `string` + + The name of the API key. + + --- + + - `subject` + - `string` + + The user or organization ID that the API key is associated with. + + --- + + - `scopes` + - `string[]` + + An array of scopes that define what the API key can access. + + --- + + - `claims` + - `Record | null` + + Custom claims associated with the API key. + + --- + + - `revoked` + - `boolean` + + A boolean indicating whether the API key has been revoked. + + --- + + - `revocationReason` + - `string | null` + + The reason for revoking the API key, if it has been revoked. + + --- + + - `expired` + - `boolean` + + A boolean indicating whether the API key has expired. + + --- + + - `expiration` + - `Date | null` + + The expiration date and time of the API key. `null` if the API key never expires. + + --- + + - `createdBy` + - `string | null` + + The ID of the user who created the API key. + + --- + + - `description` + - `string | null` + + An optional description for the API key. + + --- + + - `secret` + - `string | undefined` + + The API key secret. **This property is only present in the response from [`create()`](/docs/reference/javascript/api-keys#create) and cannot be retrieved later.** + + --- + + - `lastUsedAt` + - `Date | null` + + The date and time when the API key was last used to authenticate a request. + + --- + + - `createdAt` + - `Date` + + The date when the API key was created. + + --- + + - `updatedAt` + - `Date` + + The date when the API key was last updated. + + From 0ce85ebd121c5a3871574938e220d0e3b21bf157 Mon Sep 17 00:00:00 2001 From: wobsoriano Date: Mon, 8 Dec 2025 10:57:38 -0800 Subject: [PATCH 12/46] chore: consistent casing --- .../custom-flows/api-keys/manage-api-keys.mdx | 48 ++++++++----------- docs/reference/javascript/api-keys.mdx | 1 - docs/reference/javascript/types/api-key.mdx | 1 - 3 files changed, 21 insertions(+), 29 deletions(-) diff --git a/docs/guides/development/custom-flows/api-keys/manage-api-keys.mdx b/docs/guides/development/custom-flows/api-keys/manage-api-keys.mdx index 020395283a..6132595b62 100644 --- a/docs/guides/development/custom-flows/api-keys/manage-api-keys.mdx +++ b/docs/guides/development/custom-flows/api-keys/manage-api-keys.mdx @@ -12,9 +12,9 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for The following example: 1. Uses the [`useAPIKeys()`](/docs/reference/hooks/use-api-keys) hook from `@clerk/nextjs/experimental` to retrieve and manage API keys. It will use `subject` if provided; otherwise it falls back to the active Organization, then the current user. - 2. Displays the API keys in a table with options to create new keys and revoke existing ones. - 3. Provides a form to create new API keys using [`clerk.apiKeys.create()`](/docs/reference/javascript/api-keys#create) with an optional expiration (subject defaults to active org, then user). - 4. Allows revoking API keys using [`clerk.apiKeys.revoke()`](/docs/reference/javascript/api-keys#revoke). + 1. Displays the API keys in a table with options to create new keys and revoke existing ones. + 1. Provides a form to create new API keys using [`clerk.apiKeys.create()`](/docs/reference/javascript/api-keys#create) with an optional expiration (subject defaults to active org, then user). + 1. Allows revoking API keys using [`clerk.apiKeys.revoke()`](/docs/reference/javascript/api-keys#revoke). This example is written for Next.js App Router but can be adapted for any React-based framework. @@ -40,9 +40,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for try { const expiration = - formData.expirationSeconds.trim() === '' - ? null - : Number(formData.expirationSeconds) + formData.expirationSeconds.trim() === '' ? null : Number(formData.expirationSeconds) const newApiKey = await clerk.apiKeys.create({ name: formData.name, @@ -50,7 +48,9 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for }) // Store the secret immediately - it won't be available again - alert(`API key created! Secret: ${newApiKey.secret}\n\nMake sure to save this secret - it won't be shown again.`) + alert( + `API key created! Secret: ${newApiKey.secret}\n\nMake sure to save this secret - it won't be shown again.`, + ) setFormData({ name: '', expirationSeconds: '' }) setShowCreateForm(false) @@ -97,9 +97,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for - setFormData({ ...formData, name: e.target.value }) - } + onChange={(e) => setFormData({ ...formData, name: e.target.value })} required /> @@ -111,9 +109,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for type="number" min="0" value={formData.expirationSeconds} - onChange={(e) => - setFormData({ ...formData, expirationSeconds: e.target.value }) - } + onChange={(e) => setFormData({ ...formData, expirationSeconds: e.target.value })} />
@@ -145,9 +141,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for {!apiKey.revoked && !apiKey.expired && ( - + )} @@ -165,9 +159,9 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for The following example: 1. Calls [`clerk.apiKeys.getAll()`](/docs/reference/javascript/api-keys#getall) to retrieve the list of API keys for the active user or organization. - 2. Displays the API keys in a table. - 3. Provides a form to create new API keys using [`clerk.apiKeys.create()`](/docs/reference/javascript/api-keys#create). - 4. Allows revoking API keys using [`clerk.apiKeys.revoke()`](/docs/reference/javascript/api-keys#revoke). + 1. Displays the API keys in a table. + 1. Provides a form to create new API keys using [`clerk.apiKeys.create()`](/docs/reference/javascript/api-keys#create). + 1. Allows revoking API keys using [`clerk.apiKeys.revoke()`](/docs/reference/javascript/api-keys#revoke). Use the following tabs to view the code necessary for the `index.html` and `main.js` files. @@ -186,7 +180,7 @@ This guide will demonstrate how to use the Clerk API to build a custom flow for

API Keys

-