BETTER-AUTH. UI
Plugins

API Key

Install Solid/Zaidan API key management with create, copy, and revoke flows.

The API key plugin adds programmatic API key management to your Solid/Zaidan authentication settings. Users can create, reveal once, copy, list, and revoke API keys from security and organization settings. New keys can use a configurable expiration interval, and each listed key shows when it expires.

It contributes:

  • An <ApiKeys /> card rendered by the copied SecuritySettings component when the API Key plugin is enabled
  • An <OrganizationApiKeys /> card for organization-owned keys when apiKeyPlugin({ organization: true }) is registered
  • Solid query and mutation wiring through listApiKeysOptions, createApiKeyOptions, and deleteApiKeyOptions
  • App-owned API key cards, empty state, loading placeholder, create dialog, new-key reveal dialog, and delete confirmation dialog

Setup

Install the Better Auth plugin

Install the @better-auth/api-key package and add it to your Better Auth server config:

src/lib/auth.ts
import { apiKey } from "@better-auth/api-key"
import { betterAuth } from "better-auth"

export const auth = betterAuth({
  // ...
  plugins: [
    apiKey() 
  ]
})

Install the matching client plugin

Add apiKeyClient() to your Solid auth client so authClient.apiKey.* methods are available:

src/lib/auth-client.ts
import { apiKeyClient } from "@better-auth/api-key/client"
import { createAuthClient } from "@better-auth-ui/solid"

export const authClient = createAuthClient({
  plugins: [
    apiKeyClient() 
  ]
})

Install the Solid/Zaidan components

Run the shadcn CLI to install the Solid API key components and the apiKeyPlugin() factory into your project:

npx shadcn@latest add https://better-auth-ui.com/r/solid/api-key.json

This drops the following into your codebase:

  • src/lib/auth/api-key-plugin.ts — local apiKeyPlugin() factory
  • src/components/auth/api-key/api-keys.tsx — the API keys security card
  • src/components/auth/api-key/api-key.tsx — individual API key row with delete control
  • src/components/auth/api-key/api-keys-empty.tsx — empty state shown when no keys exist
  • src/components/auth/api-key/api-key-skeleton.tsx — loading placeholder shown while keys are loading
  • src/components/auth/api-key/create-api-key-dialog.tsx — dialog for creating a new key
  • src/components/auth/api-key/new-api-key-dialog.tsx — dialog showing the newly created key with copy button
  • src/components/auth/api-key/delete-api-key-dialog.tsx — confirmation dialog for revoking a key
  • src/components/auth/api-key/organization-api-keys.tsx — owner-gated wrapper that renders <ApiKeys /> scoped to the active organization

Register the UI plugin

For user-owned keys, the copied SecuritySettings component renders <ApiKeys /> whenever the API Key plugin is enabled. For organization-owned keys, register the copied UI plugin with { organization: true }:

src/components/providers.tsx
import { apiKeyPlugin } from "@/lib/auth/api-key-plugin"

<AuthProvider
  authClient={authClient}
  plugins={[
    apiKeyPlugin({ organization: true }) 
  ]}
>
  {children}
</AuthProvider>

Configure expiration

The create dialog offers 30 days, 90 days, and Never by default. It initially selects 30 days. Configure the choices through the copied apiKeyPlugin():

src/components/providers.tsx
apiKeyPlugin({
  keyExpiration: {
    intervals: [7, 30, 90],
    defaultInterval: 30,
    allowNever: true
  }
})

intervals and defaultInterval use days. Better Auth receives the selected lifetime as seconds.

Keep the UI choices within the limits in your Better Auth server configuration:

src/lib/auth.ts
apiKey({
  keyExpiration: {
    minExpiresIn: 7,
    maxExpiresIn: 90,
    defaultExpiresIn: null
  }
})

When allowNever is enabled, selecting Never sends no custom interval. Better Auth will still apply defaultExpiresIn if the server defines one, so set allowNever: false in the UI when your server always requires expiration.

To remove the expiration field and rely entirely on the server default:

src/components/providers.tsx
apiKeyPlugin({ keyExpiration: false })

Components

<ApiKeys />

The <ApiKeys /> security card is rendered on the security settings page when your settings layout includes the copied SecuritySettings component and the API Key plugin is enabled. It uses the Solid runtime APIs from @better-auth-ui/solid to list, create, and revoke keys.

Usage

import { ApiKeys } from "@/components/auth/api-key/api-keys"

<ApiKeys />

This component is normally mounted by SecuritySettings; render it manually only if you are building a custom settings layout.

Props

Prop

Type

<OrganizationApiKeys />

A thin wrapper around <ApiKeys /> that resolves the active organization with useActiveOrganization, checks the current user with useListOrganizationMembers, and renders nothing unless the user is an organization owner.

To enable, opt in on the copied UI plugin and add a matching API key configuration to your Better Auth server config. The organization API key config uses a fixed configId of "organization", so the server entry must be { configId: "organization", references: "organization" }:

src/components/providers.tsx
import { apiKeyPlugin } from "@/lib/auth/api-key-plugin"

<AuthProvider
  authClient={authClient}
  plugins={[
    apiKeyPlugin({ organization: true }) 
  ]}
>
  {children}
</AuthProvider>
src/lib/auth.ts
import { apiKey } from "@better-auth/api-key"
import { betterAuth } from "better-auth"
import { organization } from "better-auth/plugins"

export const auth = betterAuth({
  // ...
  plugins: [
    organization(),
    apiKey([
      { configId: "default", references: "user" },
      { configId: "organization", references: "organization" } 
    ])
  ]
})

See the Better Auth docs for role-based permissions on organization-owned keys.

Usage

import { OrganizationApiKeys } from "@/components/auth/api-key/organization-api-keys"

<OrganizationApiKeys />

Props

Prop

Type

Options

Prop

Type

Runtime API references

List API keys

Prop

Type

Create API key

Prop

Type

Delete API key

Prop

Type

Localization

Prop

Type

Last updated on

On this page