diff --git a/docs/_partials/machine-auth/api-keys-beta-callout.mdx b/docs/_partials/machine-auth/api-keys-beta-callout.mdx new file mode 100644 index 0000000000..12fb878dbb --- /dev/null +++ b/docs/_partials/machine-auth/api-keys-beta-callout.mdx @@ -0,0 +1,2 @@ +> [!WARNING] +> API keys is currently in beta. The API may change before general availability. 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..9c9085bd15 --- /dev/null +++ b/docs/guides/development/custom-flows/api-keys/manage-api-keys.mdx @@ -0,0 +1,330 @@ +--- +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()` 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](!active-organization), then the current user. + 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. + + ```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'} + + +
+
+ ) + } + ``` +
+ + + The following example: + + 1. Calls [`clerk.apiKeys.getAll()`](/docs/reference/javascript/api-keys#get-all) to retrieve the list of API keys for the active user or organization. + 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. + + + ```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) { + await loadAPIKeys() + + // 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() + } 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() { + 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() + } 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/guides/development/machine-auth/api-keys.mdx b/docs/guides/development/machine-auth/api-keys.mdx new file mode 100644 index 0000000000..2e2b3faf60 --- /dev/null +++ b/docs/guides/development/machine-auth/api-keys.mdx @@ -0,0 +1,210 @@ +--- +title: "Using API keys" +description: Learn how to use Clerk's API keys feature to let your application's users 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, imagine you're building an application like ChatGPT and you want developers on your platform to retrieve their recent conversations. You could let them generate API keys that are tied to their individual user accounts, giving them access only to their own chats. In this case, the API key would be scoped to a specific user. API keys can also be scoped to organizations or have granular scopes, such as allowing a key to read chats but not create new ones. + +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. + +## Pricing + +Before getting started, it's important to note that API keys will be a paid feature. They are currently free to use while in beta, but will be charged based on usage after the beta period comes to an end. The pricing will be as follows: + +- `$0.001` per key creation. +- `$0.0001` per key verification. + +There will be usage stats and monitoring available in the Clerk Dashboard before the beta period comes to an end to make it easy to understand your usage and what you're being charged. + +## 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 the [**API keys**](https://dashboard.clerk.com/~/platform/api-keys) page. +1. Select **Enable API keys**. +1. Select the option that you need depending on your use case: **Enable User API keys**, **Enable Organization API keys**, or both. +1. Select **Enable**. + +> [!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 [JS Backend SDK methods](#using-api-keys-via-clerks-sdks). + +If you're using Clerk's [``](/docs/reference/components/user/user-profile) or [``](/docs/reference/components/organization/organization-profile) components and have enabled API keys in the Clerk Dashboard, an **API Keys** tab will automatically appear in those profile components. This tab will allow your users to create, view, and revoke API keys, without writing any additional custom code on your end. + +If you'd prefer to hide the **API Keys** tab in the user or organization profile components, you can do so by passing a prop to the component, as such: + +```tsx + +``` + +```tsx + +``` + +```tsx + +``` + +It's also possible to render the [``](/docs/reference/components/api-keys) component directly if you want to embed the API key management UI within your own application's interface. + +> [!TIP] +> Hiding the UI doesn't prevent users from creating API keys - they can still generate them through Clerk's Frontend API. If you want to ensure only backend-issued API keys are valid, include the `scopes` or `claims` parameters when [creating your keys](#creating-api-keys). Your verification logic can then reject any keys missing the required scopes or claims, making user-generated keys unusable. + +## 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](/docs/guides/development/verifying-api-keys) guide 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. This guide uses the [JS 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: + +```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', // optional + scopes: ['read:users', 'write:users'], // optional + secondsUntilExpiration: 86400, // optional: expires in 24 hours +}) +``` + +The method returns an object containing the API key details, including the `secret` property with the actual API key value: + +```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_xxx" +} +``` + +> [!WARNING] +> 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 + +- `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 expires. By default, API keys never expire. They are typically long-lived tokens since automatic expiration could cause third-party services using them to break unexpectedly. Instead, API keys can be instantly revoked if there is a security issue - this is possible because they are opaque tokens rather than [JSON Web Tokens (JWTs)](https://en.wikipedia.org/wiki/JSON_Web_Token). + +### 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 +} +``` + +### Verifying API keys + +To verify an API key secret, call the [`verify()`](/docs/reference/backend/api-keys/verify) method: + +```ts +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. + +The following example demonstrates how to handle verification errors: + +```ts +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 +} +``` + +> [!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 + +> [!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. + +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. diff --git a/docs/guides/development/machine-auth/overview.mdx b/docs/guides/development/machine-auth/overview.mdx index 027f10d02a..a00eaa5290 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 @@ -25,6 +25,8 @@ To learn more about machine authentication with M2M tokens, see the [M2M tokens] ## API keys -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. +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. -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). +With this feature, you will be able to allow users to create API keys directly through the [``](/docs/reference/components/user/user-profile) component, 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. + +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..964dcd8f42 --- /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` and `claims` are returned. + +```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 (!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 the token is invalid or missing + if (!isAuthenticated) { + return NextResponse.json({ error: 'Unauthorized' }, { 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 + // 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 bff85c8ec1..93a63c9cac 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -639,6 +639,11 @@ { "title": "Using M2M tokens", "href": "/docs/guides/development/machine-auth/m2m-tokens" + }, + { + "title": "Using API keys", + "href": "/docs/guides/development/machine-auth/api-keys", + "tag": "(Beta)" } ] ], @@ -851,6 +856,11 @@ "title": "Verifying OAuth access tokens", "href": "/docs/guides/development/verifying-oauth-access-tokens" }, + { + "title": "Verifying API keys", + "href": "/docs/guides/development/verifying-api-keys", + "tag": "(Beta)" + }, { "title": "SPA Mode", "href": "/docs/guides/development/spa-mode" @@ -1186,6 +1196,19 @@ } ] ] + }, + { + "title": "API keys", + "tag": "(Beta)", + "collapse": false, + "items": [ + [ + { + "title": "Manage API keys", + "href": "/docs/guides/development/custom-flows/api-keys/manage-api-keys" + } + ] + ] } ] ], @@ -1708,6 +1731,11 @@ "title": "`Organization`", "href": "/docs/reference/javascript/organization" }, + { + "title": "`APIKeys`", + "href": "/docs/reference/javascript/api-keys", + "tag": "(Beta)" + }, { "title": "Types", "items": [ @@ -1780,6 +1808,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" @@ -2774,6 +2806,50 @@ ] ] }, + { + "title": "API Keys", + "tag": "(Beta)", + "collapse": true, + "items": [ + [ + { + "title": "`list()`", + "wrap": false, + "href": "/docs/reference/backend/api-keys/list" + }, + { + "title": "`get()`", + "wrap": false, + "href": "/docs/reference/backend/api-keys/get" + }, + { + "title": "`create()`", + "wrap": false, + "href": "/docs/reference/backend/api-keys/create" + }, + { + "title": "`update()`", + "wrap": false, + "href": "/docs/reference/backend/api-keys/update" + }, + { + "title": "`delete()`", + "wrap": false, + "href": "/docs/reference/backend/api-keys/delete" + }, + { + "title": "`revoke()`", + "wrap": false, + "href": "/docs/reference/backend/api-keys/revoke" + }, + { + "title": "`verify()`", + "wrap": false, + "href": "/docs/reference/backend/api-keys/verify" + } + ] + ] + }, { "title": "OAuth applications", "collapse": true, @@ -3239,6 +3315,20 @@ ] ] }, + { + "title": "API Keys components", + "collapse": false, + "items": [ + [ + { + "title": "``", + "wrap": false, + "href": "/docs/reference/components/api-keys", + "tag": "(Beta)" + } + ] + ] + }, { "title": "Control components", "collapse": false, diff --git a/docs/reference/backend/api-keys/create.mdx b/docs/reference/backend/api-keys/create.mdx new file mode 100644 index 0000000000..48b43d2a84 --- /dev/null +++ b/docs/reference/backend/api-keys/create.mdx @@ -0,0 +1,100 @@ +--- +title: '`create()`' +description: Use Clerk's JS Backend SDK to create an API key. +sdk: js-backend +--- + + + +Creates a new [API key](/docs/guides/development/machine-auth/api-keys). Returns an [`APIKeyResource`](/docs/reference/javascript/types/api-key) object. + +```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` + + Additional custom claims 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` + + Number of seconds until the API key expires. Defaults to `null` (API key does not expire). + + +## Example + + + +### Create a basic API key + +```tsx +const userId = 'user_123' + +const apiKey = await clerkClient.apiKeys.create({ + name: 'My API Key', + subject: userId, +}) +``` + +### Create an API key with optional parameters + +```tsx +const userId = 'user_123' + +const apiKey = await clerkClient.apiKeys.create({ + name: 'Production API Key', + subject: userId, + 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/delete.mdx b/docs/reference/backend/api-keys/delete.mdx new file mode 100644 index 0000000000..12cb31da98 --- /dev/null +++ b/docs/reference/backend/api-keys/delete.mdx @@ -0,0 +1,36 @@ +--- +title: '`delete()`' +description: Use Clerk's JS Backend SDK to delete an API key. +sdk: js-backend +--- + + + +Deletes an [API key](/docs/guides/development/machine-auth/api-keys) by its ID. Returns a [`DeletedObjectResource`](/docs/reference/javascript/types/deleted-object-resource) object. + +```ts {{ prettier: false }} +function delete(apiKeyId: string): Promise +``` + +## Parameters + + + - `apiKeyId` + - `string` + + The ID of the API key to delete. + + +## Example + + + +```tsx +const apiKeyId = 'apikey_123' + +const response = await clerkClient.apiKeys.delete(apiKeyId) +``` + +## Backend API (BAPI) endpoint + +This method in the SDK is a wrapper around the BAPI endpoint `DELETE/api_keys/{apiKeyID}`. See the [BAPI reference](/docs/reference/backend-api/tag/api-keys/delete/api_keys/%7BapiKeyID%7D){{ target: '_blank' }} for more information. diff --git a/docs/reference/backend/api-keys/get.mdx b/docs/reference/backend/api-keys/get.mdx new file mode 100644 index 0000000000..71e94a9209 --- /dev/null +++ b/docs/reference/backend/api-keys/get.mdx @@ -0,0 +1,36 @@ +--- +title: '`get()`' +description: Use Clerk's JS Backend SDK to retrieve an API key. +sdk: js-backend +--- + + + +Retrieves a specific [API key](/docs/guides/development/machine-auth/api-keys) by its ID. Returns an [`APIKeyResource`](/docs/reference/javascript/types/api-key) object. + +```ts +function get(apiKeyId: string): Promise +``` + +## Parameters + + + - `apiKeyId` + - `string` + + The ID of the API key to retrieve. + + +## Example + + + +```tsx +const apiKeyId = 'apikey_123' + +const response = await clerkClient.apiKeys.get(apiKeyId) +``` + +## Backend API (BAPI) endpoint + +This method in the SDK is a wrapper around the BAPI endpoint `GET/api_keys/{apiKeyId}`. See the [BAPI reference](/docs/reference/backend-api/tag/api-keys/get/api_keys/%7BapiKeyID%7D){{ 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..ed46ea3541 --- /dev/null +++ b/docs/reference/backend/api-keys/list.mdx @@ -0,0 +1,86 @@ +--- +title: '`list()`' +description: Use Clerk's JS Backend SDK to list API keys. +sdk: js-backend +--- + + + +Retrieves a list of API keys for a given user or organization. Returns a [`PaginatedResourceResponse`](/docs/reference/backend/types/paginated-resource-response) object. + +```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 + +```tsx +const userId = 'user_123' + +const apiKeys = await clerkClient.apiKeys.list({ + subject: userId, +}) +``` + +### List API keys for a user, including invalid ones + +```tsx +const userId = 'user_123' + +const apiKeys = await clerkClient.apiKeys.list({ + subject: userId, + includeInvalid: true, +}) +``` + +### List API keys for a user with pagination + +```tsx +const userId = 'user_123' + +const apiKeys = await clerkClient.apiKeys.list({ + subject: userId, + 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..ef5378c7a0 --- /dev/null +++ b/docs/reference/backend/api-keys/revoke.mdx @@ -0,0 +1,61 @@ +--- +title: '`revoke()`' +description: Use Clerk's JS Backend SDK to revoke an API key. +sdk: js-backend +--- + + + +Revokes an [API key](/docs/guides/development/machine-auth/api-keys) by its ID. This will immediately invalidate the API key and prevent it from being used to authenticate any future requests. Returns an [`APIKeyResource`](/docs/reference/javascript/types/api-key) object. + +```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 + +```tsx +const apiKeyId = 'apikey_123' + +const response = await clerkClient.apiKeys.revoke({ + apiKeyId: apiKeyId, +}) +``` + +### Revoke an API key with a reason + +```tsx +const apiKeyId = 'apikey_123' + +const response = await clerkClient.apiKeys.revoke({ + apiKeyId: apiKeyId, + 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/update.mdx b/docs/reference/backend/api-keys/update.mdx new file mode 100644 index 0000000000..3d8dd18ac0 --- /dev/null +++ b/docs/reference/backend/api-keys/update.mdx @@ -0,0 +1,78 @@ +--- +title: '`update()`' +description: Use Clerk's JS Backend SDK to update an API key. +sdk: js-backend +--- + + + +Updates an [API key](/docs/guides/development/machine-auth/api-keys) by its ID. Returns an [`APIKeyResource`](/docs/reference/javascript/types/api-key) object. + +```ts +function update(params: UpdateAPIKeyParams): Promise +``` + +## `UpdateAPIKeyParams` + + + - `apiKeyID` + - `string` + + The ID of the API key to update. + + --- + + - `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` + + Additional custom claims to store additional information about the API key. + + --- + + - `secondsUntilExpiration?` + - `number | null` + + Number of seconds until the API key expires. Defaults to `null` (API key does not expire). + + +## Example + + + +```tsx +const apiKeyId = 'apikey_123' +const userId = 'user_123' + +const response = await clerkClient.apiKeys.update({ + apiKeyId: apiKeyId, + subject: userId, + description: 'API key for accessing my application', + scopes: ['read:users', 'write:users'], + secondsUntilExpiration: 86400, // expires in 24 hours +}) +``` + +## Backend API (BAPI) endpoint + +This method in the SDK is a wrapper around the BAPI endpoint `PATCH/api_keys/{apiKeyID}`. See the [BAPI reference](/docs/reference/backend-api/tag/api-keys/patch/api_keys/%7BapiKeyID%7D){{ target: '_blank' }} for more information. diff --git a/docs/reference/backend/api-keys/verify.mdx b/docs/reference/backend/api-keys/verify.mdx new file mode 100644 index 0000000000..d487fbd8f8 --- /dev/null +++ b/docs/reference/backend/api-keys/verify.mdx @@ -0,0 +1,42 @@ +--- +title: '`verify()`' +description: Use Clerk's JS Backend SDK to verify an API key secret. +sdk: js-backend +--- + + + +Verifies an [API key](/docs/guides/development/machine-auth/api-keys) secret. Returns an [`APIKeyResource`](/docs/reference/javascript/types/api-key) object. + +- 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](/docs/guides/development/verifying-api-keys) guide for more details. + +```ts +function verify(secret: string): Promise +``` + +## Parameters + + + - `secret` + - `string` + + The API key secret to verify. + + +## Example + + + +```tsx +const apiKeySecret = 'ak_live_123' + +const response = await clerkClient.apiKeys.verify(apiKeySecret) +``` + +## 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. diff --git a/docs/reference/components/api-keys.mdx b/docs/reference/components/api-keys.mdx new file mode 100644 index 0000000000..77394764a6 --- /dev/null +++ b/docs/reference/components/api-keys.mdx @@ -0,0 +1,245 @@ +--- +title: '`` component' +description: Clerk's component renders a UI to manage API keys. +sdk: astro, chrome-extension, nextjs, nuxt, react, react-router, tanstack-react-start, vue, js-frontend +--- + + + +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](!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. Refer to the [Using API keys](/docs/guides/development/machine-auth/api-keys) guide for more information. + + + ## Example + + 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 + } + ``` + + + + ```astro {{ filename: 'pages/api-keys.astro' }} + --- + import { APIKeys } from '@clerk/astro/components' + --- + + + ``` + + + + ```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 + } + ``` + + + + ```jsx {{ filename: 'src/routes/api-keys.tsx' }} + import { APIKeys } from '@clerk/chrome-extension' + + 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' }} + + + + ``` + + + + ```vue {{ filename: 'api-keys.vue' }} + + + + ``` + + + + + ## Usage with JavaScript + + 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 have followed the [quickstart](/docs/js-frontend/getting-started/quickstart) in order to add Clerk to your JavaScript application. + + ### `mountAPIKeys()` + + Render the `` component to an HTML `
` element. + + ```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, { + perPage: 10, + showDescription: true, + }) + ``` + + ### `unmountAPIKeys()` + + Unmount and run cleanup on an existing `` component instance. + + ```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) + ``` + + +## Properties + +All props are optional. + + + - `perPage?` + - `number` + + 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](/docs/guides/customizing-clerk/appearance-prop/overview) | undefined + + 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. + + --- + + - `fallback?` + - `ReactNode` + + An optional element to be rendered while the component is mounting. + + +## Customization + +To learn about how to customize Clerk components, see the [customization documentation](/docs/guides/customizing-clerk/appearance-prop/overview). + +If Clerk's prebuilt components don't meet your specific needs or if you require more control over the logic, you can rebuild the existing Clerk flows using the Clerk API. For more information, see the [custom flow guides](/docs/guides/development/custom-flows/overview). diff --git a/docs/reference/javascript/api-keys.mdx b/docs/reference/javascript/api-keys.mdx new file mode 100644 index 0000000000..5957717b10 --- /dev/null +++ b/docs/reference/javascript/api-keys.mdx @@ -0,0 +1,149 @@ +--- +title: '`APIKeys`' +description: The APIKeys object 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` object 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](!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](!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](!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..68854f2d98 --- /dev/null +++ b/docs/reference/javascript/types/api-key.mdx @@ -0,0 +1,123 @@ +--- +title: '`APIKeyResource`' +description: An interface that describes an API key used to authenticate requests to your application's API endpoints. +sdk: js-frontend +--- + + + +An interface that describes an API key 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 API key's name. + + --- + + - `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 user ID for the user creating 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. +