{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "api-key",
  "title": "API Key",
  "description": "API Key plugin: list, create, copy, and revoke API keys. Registers an API Keys security card in account settings.",
  "dependencies": [
    "@better-auth-ui/react@latest",
    "@better-auth-ui/core@latest",
    "@better-auth/api-key",
    "lucide-react"
  ],
  "registryDependencies": [
    "alert-dialog",
    "button",
    "card",
    "dialog",
    "empty",
    "field",
    "input",
    "input-group",
    "item",
    "label",
    "select",
    "separator",
    "skeleton",
    "sonner",
    "spinner"
  ],
  "files": [
    {
      "path": "src/lib/auth/auth-plugin.ts",
      "content": "import type {\n  AuthPluginComponents,\n  AuthPlugin as AuthPluginPrimitive\n} from \"@better-auth-ui/react\"\n\ndeclare module \"@better-auth-ui/core\" {\n  /** Widens `useAuth().plugins` to the shadcn-typed `AuthPlugin`. */\n  interface AuthPluginRegister {\n    shadcn: AuthPlugin\n  }\n}\n\n/** Props the shadcn `<Auth>` router spreads onto plugin-contributed auth views. */\nexport type AuthViewProps = {\n  className?: string\n  socialLayout?: \"auto\" | \"horizontal\" | \"vertical\" | \"grid\"\n  socialPosition?: \"top\" | \"bottom\"\n}\n\n/** Props the shadcn `<Settings>` router spreads onto plugin-contributed settings views. */\nexport type SettingsViewProps = {\n  className?: string\n}\n\n/** Shadcn plugin type. Plugin authors import this from `@/lib/auth/auth-plugin`. */\nexport type AuthPlugin = AuthPluginPrimitive<\n  AuthPluginComponents,\n  AuthViewProps,\n  SettingsViewProps\n>\n",
      "type": "registry:lib",
      "target": "@lib/auth/auth-plugin.ts"
    },
    {
      "path": "src/lib/auth/api-key-plugin.ts",
      "content": "import { createAuthPlugin } from \"@better-auth-ui/core\"\nimport {\n  type ApiKeyPluginOptions,\n  apiKeyPlugin as coreApiKeyPlugin\n} from \"@better-auth-ui/core/plugins\"\n\nimport { ApiKeys } from \"@/components/auth/api-key/api-keys\"\nimport { OrganizationApiKeys } from \"@/components/auth/api-key/organization-api-keys\"\n\nexport const apiKeyPlugin = createAuthPlugin(\n  coreApiKeyPlugin.id,\n  (options: ApiKeyPluginOptions = {}) => {\n    const core = coreApiKeyPlugin(options)\n\n    return {\n      ...core,\n      securityCards: [ApiKeys],\n      ...(core.organization ? { organizationCards: [OrganizationApiKeys] } : {})\n    }\n  }\n)\n",
      "type": "registry:lib",
      "target": "@lib/auth/api-key-plugin.ts"
    },
    {
      "path": "src/components/auth/api-key/api-keys.tsx",
      "content": "\"use client\"\n\nimport {\n  type ApiKeyAuthClient,\n  useAuth,\n  useAuthPlugin,\n  useListApiKeys\n} from \"@better-auth-ui/react\"\nimport { Fragment, useState } from \"react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport { Card, CardContent } from \"@/components/ui/card\"\nimport { ItemGroup, ItemSeparator } from \"@/components/ui/item\"\nimport { apiKeyPlugin } from \"@/lib/auth/api-key-plugin\"\nimport { cn } from \"@/lib/utils\"\nimport { ApiKey } from \"./api-key\"\nimport { ApiKeySkeleton } from \"./api-key-skeleton\"\nimport { ApiKeysEmpty } from \"./api-keys-empty\"\nimport { CreateApiKeyDialog } from \"./create-api-key-dialog\"\n\nexport type ApiKeysProps = {\n  className?: string\n  /** Scope the list and create payload to an organization. */\n  organizationId?: string\n  /** Force the loading skeleton and disable the list query. */\n  isPending?: boolean\n  /** Hide the \"Create API key\" button (header + empty state). */\n  hideCreate?: boolean\n  /** Hide the per-row delete button on listed keys. */\n  hideDelete?: boolean\n}\n\nexport function ApiKeys({\n  className,\n  organizationId,\n  isPending: isPendingProp,\n  hideCreate,\n  hideDelete\n}: ApiKeysProps) {\n  const { authClient } = useAuth()\n  const { localization: apiKeyLocalization } = useAuthPlugin(apiKeyPlugin)\n\n  const { data: listData, isPending: isListPending } = useListApiKeys(\n    authClient as ApiKeyAuthClient,\n    {\n      enabled: !isPendingProp,\n      ...(organizationId\n        ? { query: { organizationId, configId: \"organization\" } }\n        : {})\n    }\n  )\n\n  const isPending = isPendingProp || isListPending\n\n  const [createOpen, setCreateOpen] = useState(false)\n\n  return (\n    <div className={cn(\"flex flex-col gap-3\", className)}>\n      <div className=\"flex items-end justify-between gap-3\">\n        <h2 className=\"truncate text-sm font-semibold\">\n          {apiKeyLocalization.apiKeys}\n        </h2>\n\n        {!hideCreate && (\n          <Button\n            className=\"shrink-0\"\n            size=\"sm\"\n            disabled={isPending}\n            onClick={() => setCreateOpen(true)}\n          >\n            {apiKeyLocalization.createApiKey}\n          </Button>\n        )}\n      </div>\n\n      <Card className=\"p-0\">\n        <CardContent className=\"p-0\">\n          {isPending ? (\n            <ApiKeySkeleton />\n          ) : !listData?.apiKeys.length ? (\n            <ApiKeysEmpty\n              onCreatePress={() => setCreateOpen(true)}\n              hideCreate={hideCreate}\n            />\n          ) : (\n            <ItemGroup className=\"gap-0\">\n              {listData.apiKeys.map((key, index) => (\n                <Fragment key={key.id}>\n                  {index > 0 && <ItemSeparator />}\n                  <ApiKey\n                    apiKey={key}\n                    hideDelete={hideDelete}\n                    organizationId={organizationId}\n                  />\n                </Fragment>\n              ))}\n            </ItemGroup>\n          )}\n        </CardContent>\n      </Card>\n\n      {!hideCreate && (\n        <CreateApiKeyDialog\n          open={createOpen}\n          onOpenChange={setCreateOpen}\n          organizationId={organizationId}\n        />\n      )}\n    </div>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/api-key/api-keys.tsx"
    },
    {
      "path": "src/components/auth/api-key/api-key.tsx",
      "content": "\"use client\"\n\nimport {\n  type ListedApiKey,\n  useAuth,\n  useAuthPlugin\n} from \"@better-auth-ui/react\"\nimport { Key, X } from \"lucide-react\"\nimport { useState } from \"react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Item,\n  ItemActions,\n  ItemContent,\n  ItemDescription,\n  ItemMedia,\n  ItemTitle\n} from \"@/components/ui/item\"\nimport { apiKeyPlugin } from \"@/lib/auth/api-key-plugin\"\nimport { DeleteApiKeyDialog } from \"./delete-api-key-dialog\"\n\nexport type ApiKeyProps = {\n  apiKey: ListedApiKey\n  /** Hide the row's delete button (e.g., when caller lacks `apiKey:delete`). */\n  hideDelete?: boolean\n  /** Scope the delete payload to an organization (sets `configId`). */\n  organizationId?: string\n}\n\nexport function ApiKey({ apiKey, hideDelete, organizationId }: ApiKeyProps) {\n  const { localization } = useAuth()\n  const { localization: apiKeyLocalization } = useAuthPlugin(apiKeyPlugin)\n  const [deleteOpen, setDeleteOpen] = useState(false)\n\n  const preview = `${apiKey.start}${\"*\".repeat(16)}`\n\n  return (\n    <Item>\n      <ItemMedia variant=\"icon\">\n        <Key />\n      </ItemMedia>\n      <ItemContent>\n        <ItemTitle>{apiKey.name || apiKeyLocalization.apiKey}</ItemTitle>\n        <ItemDescription className=\"font-mono\">{preview}</ItemDescription>\n        <ItemDescription>\n          {apiKeyLocalization.created}{\" \"}\n          {new Date(apiKey.createdAt).toLocaleString(undefined, {\n            dateStyle: \"medium\",\n            timeStyle: \"short\"\n          })}\n        </ItemDescription>\n        <ItemDescription>\n          {apiKey.expiresAt\n            ? `${apiKeyLocalization.expires} ${new Date(\n                apiKey.expiresAt\n              ).toLocaleString(undefined, {\n                dateStyle: \"medium\",\n                timeStyle: \"short\"\n              })}`\n            : apiKeyLocalization.neverExpires}\n        </ItemDescription>\n      </ItemContent>\n      <ItemActions>\n        {!hideDelete && (\n          <>\n            <Button\n              variant=\"outline\"\n              size=\"sm\"\n              onClick={() => setDeleteOpen(true)}\n              aria-label={apiKeyLocalization.deleteApiKey}\n            >\n              <X />\n\n              {localization.settings.delete}\n            </Button>\n\n            <DeleteApiKeyDialog\n              open={deleteOpen}\n              onOpenChange={setDeleteOpen}\n              apiKey={apiKey}\n              organizationId={organizationId}\n            />\n          </>\n        )}\n      </ItemActions>\n    </Item>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/api-key/api-key.tsx"
    },
    {
      "path": "src/components/auth/api-key/api-keys-empty.tsx",
      "content": "\"use client\"\n\nimport { useAuthPlugin } from \"@better-auth-ui/react\"\nimport { Key } from \"lucide-react\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Empty,\n  EmptyContent,\n  EmptyDescription,\n  EmptyHeader,\n  EmptyMedia,\n  EmptyTitle\n} from \"@/components/ui/empty\"\nimport { apiKeyPlugin } from \"@/lib/auth/api-key-plugin\"\n\nexport type ApiKeysEmptyProps = {\n  onCreatePress: () => void\n  hideCreate?: boolean\n}\n\nexport function ApiKeysEmpty({ onCreatePress, hideCreate }: ApiKeysEmptyProps) {\n  const { localization: apiKeyLocalization } = useAuthPlugin(apiKeyPlugin)\n\n  return (\n    <Empty>\n      <EmptyHeader>\n        <EmptyMedia variant=\"icon\">\n          <Key />\n        </EmptyMedia>\n        <EmptyTitle>{apiKeyLocalization.noApiKeys}</EmptyTitle>\n        <EmptyDescription>\n          {apiKeyLocalization.apiKeysDescription}\n        </EmptyDescription>\n      </EmptyHeader>\n      <EmptyContent>\n        {!hideCreate && (\n          <Button size=\"sm\" onClick={onCreatePress}>\n            {apiKeyLocalization.createApiKey}\n          </Button>\n        )}\n      </EmptyContent>\n    </Empty>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/api-key/api-keys-empty.tsx"
    },
    {
      "path": "src/components/auth/api-key/api-key-skeleton.tsx",
      "content": "\"use client\"\n\nimport { Item, ItemContent, ItemMedia } from \"@/components/ui/item\"\nimport { Skeleton } from \"@/components/ui/skeleton\"\n\nexport function ApiKeySkeleton() {\n  return (\n    <Item>\n      <ItemMedia>\n        <Skeleton className=\"size-10 rounded-md\" />\n      </ItemMedia>\n      <ItemContent>\n        <Skeleton className=\"h-4 w-28\" />\n        <Skeleton className=\"h-3 w-36\" />\n        <Skeleton className=\"h-3 w-32\" />\n      </ItemContent>\n    </Item>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/api-key/api-key-skeleton.tsx"
    },
    {
      "path": "src/components/auth/api-key/create-api-key-dialog.tsx",
      "content": "\"use client\"\n\nimport { apiKeyExpirationDaysToSeconds } from \"@better-auth-ui/core/plugins\"\nimport {\n  type ApiKeyAuthClient,\n  useAuth,\n  useAuthPlugin,\n  useCreateApiKey\n} from \"@better-auth-ui/react\"\nimport { Key } from \"lucide-react\"\nimport { type SyntheticEvent, useState } from \"react\"\nimport { Button, buttonVariants } from \"@/components/ui/button\"\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle\n} from \"@/components/ui/dialog\"\nimport {\n  Field,\n  FieldError,\n  FieldGroup,\n  FieldLabel\n} from \"@/components/ui/field\"\nimport { Input } from \"@/components/ui/input\"\nimport {\n  Select,\n  SelectContent,\n  SelectGroup,\n  SelectItem,\n  SelectTrigger,\n  SelectValue\n} from \"@/components/ui/select\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { apiKeyPlugin } from \"@/lib/auth/api-key-plugin\"\nimport { NewApiKeyDialog } from \"./new-api-key-dialog\"\n\nexport type CreateApiKeyDialogProps = {\n  open: boolean\n  onOpenChange: (open: boolean) => void\n  /** Create an organization-owned key by passing the organization id. */\n  organizationId?: string\n}\n\nexport function CreateApiKeyDialog({\n  open,\n  onOpenChange,\n  organizationId\n}: CreateApiKeyDialogProps) {\n  const { authClient, localization } = useAuth()\n  const { keyExpiration, localization: apiKeyLocalization } =\n    useAuthPlugin(apiKeyPlugin)\n\n  const { mutate: createApiKey, isPending: isCreating } = useCreateApiKey(\n    authClient as ApiKeyAuthClient\n  )\n\n  const [isNewKeyDialogOpen, setIsNewKeyDialogOpen] = useState(false)\n  const [keyName, setKeyName] = useState<string | null>(null)\n  const [secretKey, setSecretKey] = useState<string | null>(null)\n\n  const handleOpenChange = (nextOpen: boolean) => {\n    if (!nextOpen) {\n      setKeyName(null)\n      setSecretKey(null)\n    }\n\n    onOpenChange(nextOpen)\n  }\n\n  const handleNewKeyDialogOpenChange = (nextOpen: boolean) => {\n    setIsNewKeyDialogOpen(nextOpen)\n\n    if (!nextOpen) {\n      setKeyName(null)\n      setSecretKey(null)\n    }\n  }\n\n  const handleSubmit = (e: SyntheticEvent<HTMLFormElement>) => {\n    e.preventDefault()\n\n    const formData = new FormData(e.target as HTMLFormElement)\n    const name = (formData.get(\"name\") as string).trim()\n    const expiration = formData.get(\"expiration\")\n    const expirationDays =\n      typeof expiration === \"string\" && expiration !== \"never\"\n        ? Number(expiration)\n        : undefined\n    const expiresIn = expirationDays\n      ? apiKeyExpirationDaysToSeconds(expirationDays)\n      : undefined\n\n    const payload = {\n      ...(name ? { name } : {}),\n      ...(expiresIn ? { expiresIn } : {}),\n      ...(organizationId ? { organizationId, configId: \"organization\" } : {})\n    }\n\n    createApiKey(Object.keys(payload).length > 0 ? payload : undefined, {\n      onSuccess: (result) => {\n        handleOpenChange(false)\n        setKeyName(name)\n        setSecretKey(result.key)\n        setIsNewKeyDialogOpen(true)\n      }\n    })\n  }\n\n  return (\n    <>\n      <Dialog open={open} onOpenChange={handleOpenChange}>\n        <DialogContent>\n          <form onSubmit={handleSubmit} className=\"flex flex-col gap-6\">\n            <DialogHeader>\n              <DialogTitle>\n                <Key />\n                {apiKeyLocalization.createApiKey}\n              </DialogTitle>\n\n              <DialogDescription>\n                {apiKeyLocalization.apiKeysDescription}\n              </DialogDescription>\n            </DialogHeader>\n\n            <FieldGroup>\n              <Field>\n                <FieldLabel htmlFor=\"api-key-name\">\n                  {apiKeyLocalization.name}\n                </FieldLabel>\n\n                <Input\n                  id=\"api-key-name\"\n                  name=\"name\"\n                  autoFocus\n                  placeholder={localization.settings.optional}\n                  disabled={isCreating}\n                />\n\n                <FieldError />\n              </Field>\n\n              {keyExpiration ? (\n                <Field>\n                  <FieldLabel htmlFor=\"api-key-expiration\">\n                    {apiKeyLocalization.expiration}\n                  </FieldLabel>\n\n                  <Select\n                    name=\"expiration\"\n                    defaultValue={\n                      keyExpiration.defaultInterval === null\n                        ? \"never\"\n                        : String(keyExpiration.defaultInterval)\n                    }\n                    disabled={isCreating}\n                  >\n                    <SelectTrigger id=\"api-key-expiration\" className=\"w-full\">\n                      <SelectValue />\n                    </SelectTrigger>\n\n                    <SelectContent>\n                      <SelectGroup>\n                        {keyExpiration.intervals.map((days) => (\n                          <SelectItem key={days} value={String(days)}>\n                            {days.toLocaleString()}{\" \"}\n                            {days === 1\n                              ? apiKeyLocalization.day\n                              : apiKeyLocalization.days}\n                          </SelectItem>\n                        ))}\n\n                        {keyExpiration.allowNever ? (\n                          <SelectItem value=\"never\">\n                            {apiKeyLocalization.never}\n                          </SelectItem>\n                        ) : null}\n                      </SelectGroup>\n                    </SelectContent>\n                  </Select>\n                </Field>\n              ) : null}\n            </FieldGroup>\n\n            <DialogFooter>\n              <DialogClose\n                className={buttonVariants({ variant: \"outline\" })}\n                disabled={isCreating}\n                type=\"button\"\n              >\n                {localization.settings.cancel}\n              </DialogClose>\n\n              <Button type=\"submit\" disabled={isCreating}>\n                {isCreating && <Spinner />}\n\n                {apiKeyLocalization.createApiKey}\n              </Button>\n            </DialogFooter>\n          </form>\n        </DialogContent>\n      </Dialog>\n\n      <NewApiKeyDialog\n        open={isNewKeyDialogOpen}\n        onOpenChange={handleNewKeyDialogOpenChange}\n        secretKey={secretKey}\n        name={keyName}\n      />\n    </>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/api-key/create-api-key-dialog.tsx"
    },
    {
      "path": "src/components/auth/api-key/new-api-key-dialog.tsx",
      "content": "\"use client\"\n\nimport { useAuth, useAuthPlugin } from \"@better-auth-ui/react\"\nimport { Check, Copy, Key } from \"lucide-react\"\nimport { useState } from \"react\"\nimport { toast } from \"sonner\"\n\nimport { Button } from \"@/components/ui/button\"\nimport {\n  Dialog,\n  DialogContent,\n  DialogDescription,\n  DialogFooter,\n  DialogHeader,\n  DialogTitle\n} from \"@/components/ui/dialog\"\nimport {\n  InputGroup,\n  InputGroupAddon,\n  InputGroupButton,\n  InputGroupInput\n} from \"@/components/ui/input-group\"\nimport { Label } from \"@/components/ui/label\"\nimport { apiKeyPlugin } from \"@/lib/auth/api-key-plugin\"\n\nexport type NewApiKeyDialogProps = {\n  open: boolean\n  onOpenChange: (open: boolean) => void\n  name: string | null\n  secretKey: string | null\n}\n\nexport function NewApiKeyDialog({\n  open,\n  onOpenChange,\n  name,\n  secretKey\n}: NewApiKeyDialogProps) {\n  const { localization } = useAuth()\n  const { localization: apiKeyLocalization } = useAuthPlugin(apiKeyPlugin)\n\n  const [copied, setCopied] = useState(false)\n\n  const handleOpenChange = (nextOpen: boolean) => {\n    if (!nextOpen) {\n      setCopied(false)\n    }\n\n    onOpenChange(nextOpen)\n  }\n\n  const copySecretKey = async () => {\n    if (!secretKey) return\n\n    try {\n      await navigator.clipboard.writeText(secretKey)\n      setCopied(true)\n      setTimeout(() => setCopied(false), 1500)\n    } catch (error) {\n      toast.error(error instanceof Error ? error.message : String(error))\n    }\n  }\n\n  return (\n    <Dialog open={open} onOpenChange={handleOpenChange}>\n      <DialogContent showCloseButton={false}>\n        <DialogHeader>\n          <DialogTitle>\n            <Key />\n            {apiKeyLocalization.newApiKey}\n          </DialogTitle>\n\n          <DialogDescription>\n            {apiKeyLocalization.newApiKeyWarning}\n          </DialogDescription>\n        </DialogHeader>\n\n        <div className=\"flex flex-col gap-2\">\n          <Label htmlFor=\"new-api-key-secret\">\n            {name || apiKeyLocalization.apiKey}\n          </Label>\n\n          <InputGroup>\n            <InputGroupInput\n              id=\"new-api-key-secret\"\n              value={secretKey ?? \"\"}\n              readOnly\n              className=\"font-mono text-xs\"\n            />\n\n            <InputGroupAddon align=\"inline-end\">\n              <InputGroupButton\n                size=\"icon-xs\"\n                aria-label={localization.settings.copyToClipboard}\n                onClick={copySecretKey}\n              >\n                {copied ? <Check /> : <Copy />}\n              </InputGroupButton>\n            </InputGroupAddon>\n          </InputGroup>\n        </div>\n\n        <DialogFooter>\n          <Button type=\"button\" onClick={() => handleOpenChange(false)}>\n            {apiKeyLocalization.dismissNewKey}\n          </Button>\n        </DialogFooter>\n      </DialogContent>\n    </Dialog>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/api-key/new-api-key-dialog.tsx"
    },
    {
      "path": "src/components/auth/api-key/delete-api-key-dialog.tsx",
      "content": "\"use client\"\n\nimport {\n  type ApiKeyAuthClient,\n  type ListedApiKey,\n  useAuth,\n  useAuthPlugin,\n  useDeleteApiKey\n} from \"@better-auth-ui/react\"\nimport { Key } from \"lucide-react\"\n\nimport {\n  AlertDialog,\n  AlertDialogCancel,\n  AlertDialogContent,\n  AlertDialogDescription,\n  AlertDialogFooter,\n  AlertDialogHeader,\n  AlertDialogMedia,\n  AlertDialogTitle\n} from \"@/components/ui/alert-dialog\"\nimport { Button } from \"@/components/ui/button\"\nimport { Field, FieldLabel } from \"@/components/ui/field\"\nimport { Input } from \"@/components/ui/input\"\nimport { Spinner } from \"@/components/ui/spinner\"\nimport { apiKeyPlugin } from \"@/lib/auth/api-key-plugin\"\n\nexport type DeleteApiKeyDialogProps = {\n  open: boolean\n  onOpenChange: (open: boolean) => void\n  apiKey: ListedApiKey\n  /** Scope the delete payload to an organization (sets `configId`). */\n  organizationId?: string\n}\n\nexport function DeleteApiKeyDialog({\n  open,\n  onOpenChange,\n  apiKey,\n  organizationId\n}: DeleteApiKeyDialogProps) {\n  const { authClient, localization } = useAuth()\n  const { localization: apiKeyLocalization } = useAuthPlugin(apiKeyPlugin)\n  const preview = `${apiKey.start}${\"*\".repeat(16)}`\n  const previewId = `delete-api-key-preview-${apiKey.id}`\n  const { mutate: deleteApiKey, isPending: isDeleting } = useDeleteApiKey(\n    authClient as ApiKeyAuthClient,\n    {\n      onSuccess: () => onOpenChange(false)\n    }\n  )\n\n  return (\n    <AlertDialog open={open} onOpenChange={onOpenChange}>\n      <AlertDialogContent>\n        <AlertDialogHeader>\n          <AlertDialogMedia>\n            <Key />\n          </AlertDialogMedia>\n\n          <AlertDialogTitle>{apiKeyLocalization.deleteApiKey}</AlertDialogTitle>\n\n          <AlertDialogDescription>\n            {apiKeyLocalization.deleteApiKeyWarning}\n          </AlertDialogDescription>\n        </AlertDialogHeader>\n\n        <Field>\n          <FieldLabel htmlFor={previewId}>\n            {apiKey.name || apiKeyLocalization.apiKey}\n          </FieldLabel>\n\n          <Input\n            id={previewId}\n            value={preview}\n            readOnly\n            className=\"font-mono text-xs\"\n            disabled\n          />\n        </Field>\n\n        <AlertDialogFooter>\n          <AlertDialogCancel disabled={isDeleting}>\n            {localization.settings.cancel}\n          </AlertDialogCancel>\n\n          <Button\n            type=\"button\"\n            variant=\"destructive\"\n            disabled={isDeleting}\n            onClick={() =>\n              deleteApiKey({\n                keyId: apiKey.id,\n                ...(organizationId ? { configId: \"organization\" } : {})\n              })\n            }\n          >\n            {isDeleting && <Spinner />}\n\n            {apiKeyLocalization.deleteApiKey}\n          </Button>\n        </AlertDialogFooter>\n      </AlertDialogContent>\n    </AlertDialog>\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/api-key/delete-api-key-dialog.tsx"
    },
    {
      "path": "src/components/auth/api-key/organization-api-keys.tsx",
      "content": "\"use client\"\n\nimport {\n  type OrganizationAuthClient,\n  useActiveOrganization,\n  useAuth,\n  useListOrganizationMembers,\n  useSession\n} from \"@better-auth-ui/react\"\n\nimport { ApiKeys } from \"./api-keys\"\n\nexport type OrganizationApiKeysProps = {\n  className?: string\n}\n\n/**\n * {@link ApiKeys} scoped to the active organization.\n *\n * Hidden for members whose role isn't `owner`. Better Auth's\n * `/organization/has-permission` endpoint isn't usable for `apiKey:*` checks\n * (it doesn't pass `allowCreatorAllPermissions` and the default org AC has no\n * `apiKey` statements), so we gate on role directly.\n */\nexport function OrganizationApiKeys({ className }: OrganizationApiKeysProps) {\n  const { authClient } = useAuth()\n  const { data: session } = useSession(authClient)\n\n  const { data: activeOrganization, isPending: activeOrganizationPending } =\n    useActiveOrganization(authClient as OrganizationAuthClient)\n\n  const { data: membersData } = useListOrganizationMembers(\n    authClient as OrganizationAuthClient\n  )\n\n  const canManageApiKeys = membersData?.members.some(\n    (member) => member.role === \"owner\" && member.userId === session?.user.id\n  )\n\n  if (!canManageApiKeys) {\n    return null\n  }\n\n  return (\n    <ApiKeys\n      className={className}\n      organizationId={activeOrganization?.id}\n      isPending={activeOrganizationPending}\n    />\n  )\n}\n",
      "type": "registry:component",
      "target": "@components/auth/api-key/organization-api-keys.tsx"
    }
  ],
  "type": "registry:component"
}