Skip to content

Commit 5cc22b2

Browse files
authored
Merge pull request #104 from daveyplate/organizations
Organizations + v2.0.0 Refactor
2 parents 89b20cb + 2210f4f commit 5cc22b2

File tree

179 files changed

+11908
-4222
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

179 files changed

+11908
-4222
lines changed

.cursor/rules/instructions.mdc

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,8 @@ alwaysApply: true
55
---
66
Always search the codebase for relevant files and do research before you begin a task.
77

8-
Never hard code strings, always check AuthLocalization and add it as needed.
8+
Never hard code strings, always check AuthLocalization and add it as needed.
9+
10+
Icons in Buttons don't need size or Margin it's handled automatically by our shadcn Button component.
11+
12+
Don't run type checks in terminal or create temporary test files.

biome.json

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,14 +11,12 @@
1111
},
1212
"formatter": {
1313
"indentStyle": "space",
14-
"indentWidth": 4,
15-
"lineWidth": 100
14+
"indentWidth": 4
1615
},
1716
"linter": {
1817
"rules": {
1918
"a11y": {
20-
"noSvgWithoutTitle": "off",
21-
"useGenericFontNames": "off"
19+
"noSvgWithoutTitle": "off"
2220
},
2321
"suspicious": {
2422
"noArrayIndexKey": "off",

docs/app/(docs)/[[...slug]]/page.tsx

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,12 @@ import { AutoTypeTable } from "fumadocs-typescript/ui"
33

44
import { Tab, Tabs } from "fumadocs-ui/components/tabs"
55
import defaultMdxComponents from "fumadocs-ui/mdx"
6-
import { DocsBody, DocsDescription, DocsPage, DocsTitle } from "fumadocs-ui/page"
6+
import {
7+
DocsBody,
8+
DocsDescription,
9+
DocsPage,
10+
DocsTitle
11+
} from "fumadocs-ui/page"
712
import { notFound } from "next/navigation"
813

914
import { source } from "@/lib/source"

docs/app/favicon.ico

11.2 KB
Binary file not shown.

docs/app/layout.tsx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@ export const metadata = createMetadata({
1414
template: "%s | Better Auth UI",
1515
default: "Better Auth UI"
1616
},
17-
description: "UI components for the most comprehensive authentication library for TypeScript.",
17+
description:
18+
"UI components for the most comprehensive authentication library for TypeScript.",
1819
metadataBase: baseUrl
1920
})
2021

2122
export default function Layout({ children }: { children: ReactNode }) {
2223
return (
2324
<html lang="en" suppressHydrationWarning>
24-
<body className={`${geistSans.className} flex min-h-screen flex-col antialiased`}>
25+
<body
26+
className={`${geistSans.className} flex min-h-screen flex-col antialiased`}
27+
>
2528
<RootProvider
2629
search={{
2730
options: {

docs/content/docs/advanced/additional-fields.mdx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,12 @@ You may use `additionalFields` to define extra fields required during signup or
3030
validate: (value: string) => parseInt(value) >= 18
3131
}
3232
}}
33-
settingsFields={["company", "age"]}
34-
signUpFields={["company", "age"]}
33+
settings={{
34+
fields: ["company", "age"]
35+
}}
36+
signUp={{
37+
fields: ["company", "age"]
38+
}}
3539
>
3640
{children}
3741
</AuthUIProvider>
Lines changed: 221 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,221 @@
1+
---
2+
title: API Keys
3+
---
4+
5+
API Keys provide a secure way for applications and services to authenticate with your API programmatically. Better Auth UI includes a complete API key management system with creation, expiration, and revocation capabilities.
6+
7+
## Overview
8+
9+
The API key system provides:
10+
11+
- **Secure Generation**: Cryptographically secure key generation
12+
- **Expiration Management**: Set expiration dates for keys
13+
- **Custom Prefixes**: Add custom prefixes to identify key types
14+
- **Metadata Support**: Attach metadata to keys for tracking
15+
- **One-Time Display**: Keys are shown only once after creation
16+
- **Secure Storage**: Keys are hashed before storage
17+
18+
## Enabling API Keys
19+
20+
To enable API keys, configure the `apiKey` prop in your `AuthUIProvider`:
21+
22+
```tsx title="providers.tsx"
23+
<AuthUIProvider
24+
authClient={authClient}
25+
apiKey={true} // Simple enable
26+
>
27+
{children}
28+
</AuthUIProvider>
29+
```
30+
31+
### Advanced Configuration
32+
33+
```tsx title="providers.tsx"
34+
<AuthUIProvider
35+
authClient={authClient}
36+
apiKey={{
37+
prefix: "app_", // Custom prefix for all keys
38+
metadata: { // Default metadata for new keys
39+
environment: "production",
40+
version: "v1"
41+
}
42+
}}
43+
>
44+
{children}
45+
</AuthUIProvider>
46+
```
47+
48+
## Key Components
49+
50+
### APIKeysCard
51+
52+
The main component for managing API keys:
53+
54+
```tsx
55+
import { APIKeysCard } from '@daveyplate/better-auth-ui'
56+
57+
<APIKeysCard />
58+
```
59+
60+
### In SettingsCards
61+
62+
API keys automatically appear in settings when enabled:
63+
64+
```tsx
65+
import { SettingsCards } from '@daveyplate/better-auth-ui'
66+
67+
// Shows API keys management when view="API_KEYS"
68+
<SettingsCards view="API_KEYS" />
69+
```
70+
71+
## API Key Structure
72+
73+
Generated API keys follow this structure:
74+
```
75+
[prefix][random_string]
76+
```
77+
78+
Example: `app_sk_live_a1b2c3d4e5f6g7h8i9j0`
79+
80+
## Using API Keys
81+
82+
### Client-Side Generation
83+
84+
```tsx
85+
const { authClient } = useContext(AuthUIContext)
86+
87+
// Create a new API key
88+
const { key } = await authClient.apiKey.create({
89+
name: "Production API Key",
90+
expiresIn: 30 * 24 * 60 * 60, // 30 days in seconds
91+
prefix: "prod_",
92+
metadata: {
93+
service: "payment-processor"
94+
}
95+
})
96+
97+
console.log(key) // This is the only time you'll see the full key
98+
```
99+
100+
### Server-Side Validation
101+
102+
```ts
103+
// In your API endpoint
104+
import { auth } from '@/lib/auth'
105+
106+
export async function POST(request: Request) {
107+
const apiKey = request.headers.get('x-api-key')
108+
109+
if (!apiKey) {
110+
return new Response('API key required', { status: 401 })
111+
}
112+
113+
const session = await auth.api.validateAPIKey({
114+
apiKey
115+
})
116+
117+
if (!session) {
118+
return new Response('Invalid API key', { status: 401 })
119+
}
120+
121+
// Access user and metadata
122+
console.log(session.user)
123+
console.log(session.apiKey.metadata)
124+
125+
// Process request...
126+
}
127+
```
128+
129+
## Security Features
130+
131+
### One-Time Display
132+
- Keys are shown in full only once after creation
133+
- Users must copy the key immediately
134+
- Lost keys cannot be recovered
135+
136+
### Secure Storage
137+
- Keys are hashed using bcrypt before storage
138+
- Original keys are never stored in the database
139+
- Only the first few characters are stored for identification
140+
141+
### Expiration
142+
- Keys can have expiration dates
143+
- Expired keys are automatically rejected
144+
- No expiration option available for long-lived keys
145+
146+
## Best Practices
147+
148+
1. **Naming Convention**
149+
```
150+
"Production - Payment Service"
151+
"Development - Local Testing"
152+
"CI/CD - GitHub Actions"
153+
```
154+
155+
2. **Expiration Policy**
156+
- Development keys: 7-30 days
157+
- Production keys: 90-365 days
158+
- CI/CD keys: No expiration with rotation
159+
160+
3. **Key Rotation**
161+
- Rotate production keys every 90 days
162+
- Implement overlap period for smooth transition
163+
- Log key usage for audit trails
164+
165+
4. **Access Control**
166+
- Limit who can create API keys
167+
- Log all key operations
168+
- Monitor key usage patterns
169+
170+
5. **Environment Separation**
171+
```tsx
172+
apiKey={{
173+
prefix: process.env.NODE_ENV === 'production' ? 'pk_' : 'sk_test_'
174+
}}
175+
```
176+
177+
## Metadata Usage
178+
179+
Attach metadata to track key usage:
180+
181+
```tsx
182+
await authClient.apiKey.create({
183+
name: "Analytics Service",
184+
metadata: {
185+
service: "analytics",
186+
environment: "production",
187+
permissions: ["read:analytics", "write:reports"],
188+
rateLimit: 1000
189+
}
190+
})
191+
```
192+
193+
## Rate Limiting
194+
195+
Implement rate limiting based on metadata:
196+
197+
```ts
198+
// Server-side
199+
const session = await auth.api.validateAPIKey({ apiKey })
200+
const rateLimit = session.apiKey.metadata.rateLimit || 100
201+
202+
// Apply rate limiting logic
203+
```
204+
205+
## Monitoring
206+
207+
Track API key usage:
208+
209+
1. **Usage Metrics**: Track requests per key
210+
2. **Error Rates**: Monitor failed authentications
211+
3. **Expiration Alerts**: Notify before keys expire
212+
4. **Anomaly Detection**: Detect unusual usage patterns
213+
214+
## Error Handling
215+
216+
Common API key errors:
217+
218+
- `API_KEY_INVALID`: Key doesn't exist or is malformed
219+
- `API_KEY_EXPIRED`: Key has passed expiration date
220+
- `API_KEY_REVOKED`: Key has been manually revoked
221+
- `API_KEY_RATE_LIMITED`: Key has exceeded rate limits

docs/content/docs/advanced/custom-auth-paths.mdx

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -28,13 +28,13 @@ export function Providers({ children }: { children: React.ReactNode }) {
2828
onSessionChange={() => router.refresh()}
2929
Link={Link}
3030
viewPaths={{
31-
signIn: "login",
32-
signOut: "logout",
33-
signUp: "register",
34-
forgotPassword: "forgot",
35-
resetPassword: "reset",
36-
magicLink: "magic",
37-
settings: "config"
31+
SIGN_IN: "login",
32+
SIGN_OUT: "logout",
33+
SIGN_UP: "register",
34+
FORGOT_PASSWORD: "forgot",
35+
RESET_PASSWORD: "reset",
36+
MAGIC_LINK: "magic",
37+
SETTINGS: "config"
3838
}}
3939
>
4040
{children}
@@ -68,13 +68,13 @@ import { authViewPaths } from "@daveyplate/better-auth-ui/server"
6868
export function generateStaticParams() {
6969
return Object.values({
7070
...authViewPaths,
71-
signIn: "login",
72-
signOut: "logout",
73-
signUp: "register",
74-
forgotPassword: "forgot",
75-
resetPassword: "reset",
76-
magicLink: "magic",
77-
settings: "config"
71+
SIGN_IN: "login",
72+
SIGN_OUT: "logout",
73+
SIGN_UP: "register",
74+
FORGOT_PASSWORD: "forgot",
75+
RESET_PASSWORD: "reset",
76+
MAGIC_LINK: "magic",
77+
SETTINGS: "config"
7878
}).map((pathname) => ({ pathname }))
7979
}
8080

docs/content/docs/advanced/custom-settings.mdx

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,13 @@ You have two primary ways to create a custom settings page experience:
1414
1. Use the built-in plug-and-play `<SettingsCards />` component on your custom route.
1515
2. Selectively import and use individual components to build your own specific settings page layout.
1616

17-
## Step 1: Setting `settingsURL`
17+
## Step 1: Setting `settings.url`
1818

1919
To override the default built-in settings page URL (`/auth/settings`) with your custom URL (example: `/dashboard/settings`):
2020

2121
### With AuthUIProvider:
2222

23-
Set the `settingsURL` prop within your main application provider:
23+
Set the `settings.url` prop within your main application provider:
2424

2525
```tsx title="app/providers.tsx"
2626
"use client"
@@ -39,7 +39,9 @@ export function Providers({ children }: { children: React.ReactNode }) {
3939
navigate={router.push}
4040
replace={router.replace}
4141
onSessionChange={() => router.refresh()}
42-
settingsURL="/dashboard/settings" // Your custom settings route
42+
settings={{
43+
url: "/dashboard/settings" // Your custom settings route
44+
}}
4345
Link={Link}
4446
>
4547
{children}
@@ -48,7 +50,7 @@ export function Providers({ children }: { children: React.ReactNode }) {
4850
}
4951
```
5052

51-
> **Important**: Once `settingsURL` is set, the built-in `/auth/settings` route will automatically redirect users visiting `/auth/settings` to your custom path, providing a seamless replacement of the default option.
53+
> **Important**: Once `settings.url` is set, the built-in `/auth/settings` route will automatically redirect users visiting `/auth/settings` to your custom path, providing a seamless replacement of the default option.
5254
5355
## Step 2: Implementing Custom Settings UI
5456

@@ -162,8 +164,10 @@ This example assumes `additionalFields` are configured via your `<AuthUIProvider
162164
type: "number"
163165
}
164166
}}
165-
settingsFields={["age"]}
166-
settingsURL="/dashboard/settings"
167+
settings={{
168+
fields: ["age"],
169+
url: "/dashboard/settings"
170+
}}
167171
>
168172
{children}
169173
</AuthUIProvider>

0 commit comments

Comments
 (0)