@githat/nextjs 0.4.0 → 0.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -11,7 +11,7 @@ GitHat is your backend. This SDK connects your app to `api.githat.io` with pre-b
11
11
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue)](https://www.typescriptlang.org/)
12
12
  [![License](https://img.shields.io/badge/License-Proprietary-red.svg)](./LICENSE)
13
13
 
14
- [Quick Start](#quick-start) · [Components](#components) · [Hooks](#hooks) · [Middleware](#middleware) · [Docs](https://githat.io/docs)
14
+ [Quick Start](#quick-start) · [Components](#components) · [Hooks](#hooks) · [Middleware](#middleware) · [Server-Side](#server-side-authentication) · [Docs](https://githat.io/docs)
15
15
 
16
16
  </div>
17
17
 
@@ -19,9 +19,11 @@ GitHat is your backend. This SDK connects your app to `api.githat.io` with pre-b
19
19
 
20
20
  ## Features
21
21
 
22
- - **Pre-built UI Components** — Sign-in, sign-up, user button, org switcher — all themed and ready to go
23
- - **React Hooks** — `useAuth()` and `useGitHat()` for full control over auth state and API calls
22
+ - **Pre-built UI Components** — Sign-in, sign-up, password reset, email verification — all themed and ready to go
23
+ - **React Hooks** — `useAuth()`, `useGitHat()`, and `useData()` for full control over auth state and API calls
24
24
  - **Next.js Middleware** — Protect routes at the edge with a single line of config
25
+ - **Server-Side Auth** — Verify tokens, wrap API routes, and inject auth headers
26
+ - **Customer Data API** — Store and query app data in GitHat's managed DynamoDB
25
27
  - **Multi-Tenant** — Organizations, team invitations, and role-based access — managed by GitHat's backend
26
28
  - **MCP & Agent Verification** — Verify MCP servers and AI agents on-chain
27
29
  - **Dark Theme Included** — Import `@githat/nextjs/styles` for a polished dark UI out of the box
@@ -99,6 +101,10 @@ export default function SignUpPage() {
99
101
 
100
102
  ### 5. Protect Routes with Middleware
101
103
 
104
+ <!-- tabs:start -->
105
+
106
+ #### **Next.js 14-15 (middleware.ts)**
107
+
102
108
  ```ts
103
109
  // middleware.ts (project root)
104
110
  import { authMiddleware } from '@githat/nextjs/middleware';
@@ -112,6 +118,23 @@ export const config = {
112
118
  };
113
119
  ```
114
120
 
121
+ #### **Next.js 16+ (proxy.ts)**
122
+
123
+ ```ts
124
+ // proxy.ts (project root)
125
+ import { authProxy } from '@githat/nextjs/proxy';
126
+
127
+ export const proxy = authProxy({
128
+ publicRoutes: ['/', '/sign-in', '/sign-up', '/forgot-password'],
129
+ });
130
+
131
+ export const config = {
132
+ matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
133
+ };
134
+ ```
135
+
136
+ <!-- tabs:end -->
137
+
115
138
  ### 6. Use Auth State Anywhere
116
139
 
117
140
  ```tsx
@@ -142,32 +165,91 @@ That's it. You now have a fully authenticated app with sign-in, sign-up, route p
142
165
 
143
166
  ### Provider
144
167
 
145
- | Component | Description | Key Props |
146
- |------------------|------------------------------------------|---------------------------------------------------------|
168
+ | Component | Description | Key Props |
169
+ |------------------|------------------------------------------|-----------------------------------------------------------|
147
170
  | `GitHatProvider` | Wraps your app and provides auth context | `config` (required) — see [Configuration](#configuration) |
148
171
 
149
172
  ### Authentication Forms
150
173
 
151
- | Component | Description | Props |
152
- |--------------|--------------------------------------|--------------------------------------------------|
153
- | `SignInForm` | Email + password sign-in form | `onSuccess?`, `signUpUrl?`, `forgotPasswordUrl?` |
154
- | `SignUpForm` | Name + email + password sign-up form | `onSuccess?`, `signInUrl?` |
174
+ | Component | Description | Props |
175
+ |----------------------|--------------------------------------|-----------------------------------------------------|
176
+ | `SignInForm` | Email + password sign-in form | `onSuccess?`, `signUpUrl?`, `forgotPasswordUrl?` |
177
+ | `SignUpForm` | Name + email + password sign-up form | `onSuccess?`, `signInUrl?` |
178
+ | `ForgotPasswordForm` | Request password reset email | `onSuccess?`, `onError?`, `signInUrl?` |
179
+ | `ResetPasswordForm` | Reset password with token | `token`, `onSuccess?`, `onError?`, `signInUrl?`, `minPasswordLength?` |
180
+ | `VerifyEmailStatus` | Auto-verify email from URL token | `token`, `onSuccess?`, `onError?`, `signInUrl?`, `redirectDelay?` |
181
+ | `ChangePasswordForm` | Change password (authenticated) | `onSuccess?`, `onError?`, `minPasswordLength?` |
155
182
 
156
183
  ### Navigation
157
184
 
158
- | Component | Description | Props |
159
- |----------------|----------------------------------------------|------------------------------------------|
160
- | `SignInButton` | Link/button to your sign-in page | `className?`, `children?`, `href?` |
161
- | `SignUpButton` | Link/button to your sign-up page | `className?`, `children?`, `href?` |
162
- | `UserButton` | Avatar dropdown with user info and sign-out | — (uses context) |
163
- | `OrgSwitcher` | Dropdown to switch between organizations | — (uses context) |
185
+ | Component | Description | Props |
186
+ |----------------|---------------------------------------------|------------------------------------|
187
+ | `SignInButton` | Link/button to your sign-in page | `className?`, `children?`, `href?` |
188
+ | `SignUpButton` | Link/button to your sign-up page | `className?`, `children?`, `href?` |
189
+ | `UserButton` | Avatar dropdown with user info and sign-out | — (uses context) |
190
+ | `OrgSwitcher` | Dropdown to switch between organizations | — (uses context) |
164
191
 
165
192
  ### Protection & Verification
166
193
 
167
- | Component | Description | Props |
168
- |------------------|--------------------------------------------|--------------------------------------------------------------|
169
- | `ProtectedRoute` | Redirects unauthenticated users to sign-in | `children`, `fallback?` |
170
- | `VerifiedBadge` | Displays MCP/agent verification status | `type: 'mcp' \| 'agent'`, `identifier`, `label?` |
194
+ | Component | Description | Props |
195
+ |------------------|--------------------------------------------|--------------------------------------------------|
196
+ | `ProtectedRoute` | Redirects unauthenticated users to sign-in | `children`, `fallback?` |
197
+ | `VerifiedBadge` | Displays MCP/agent verification status | `type: 'mcp' \| 'agent'`, `identifier`, `label?` |
198
+
199
+ ### Example: Password Reset Flow
200
+
201
+ ```tsx
202
+ // app/forgot-password/page.tsx
203
+ import { ForgotPasswordForm } from '@githat/nextjs';
204
+
205
+ export default function ForgotPasswordPage() {
206
+ return (
207
+ <div style={{ maxWidth: 400, margin: '80px auto' }}>
208
+ <ForgotPasswordForm signInUrl="/sign-in" />
209
+ </div>
210
+ );
211
+ }
212
+ ```
213
+
214
+ ```tsx
215
+ // app/reset-password/page.tsx
216
+ 'use client';
217
+
218
+ import { ResetPasswordForm } from '@githat/nextjs';
219
+ import { useSearchParams } from 'next/navigation';
220
+
221
+ export default function ResetPasswordPage() {
222
+ const searchParams = useSearchParams();
223
+ const token = searchParams.get('token') || '';
224
+
225
+ return (
226
+ <div style={{ maxWidth: 400, margin: '80px auto' }}>
227
+ <ResetPasswordForm token={token} signInUrl="/sign-in" />
228
+ </div>
229
+ );
230
+ }
231
+ ```
232
+
233
+ ### Example: Email Verification
234
+
235
+ ```tsx
236
+ // app/verify-email/page.tsx
237
+ 'use client';
238
+
239
+ import { VerifyEmailStatus } from '@githat/nextjs';
240
+ import { useSearchParams } from 'next/navigation';
241
+
242
+ export default function VerifyEmailPage() {
243
+ const searchParams = useSearchParams();
244
+ const token = searchParams.get('token') || '';
245
+
246
+ return (
247
+ <div style={{ maxWidth: 400, margin: '80px auto' }}>
248
+ <VerifyEmailStatus token={token} signInUrl="/sign-in" />
249
+ </div>
250
+ );
251
+ }
252
+ ```
171
253
 
172
254
  ### Example: Protected Dashboard
173
255
 
@@ -256,14 +338,30 @@ export function CustomLogin() {
256
338
 
257
339
  ### `useGitHat()`
258
340
 
259
- Access the GitHat API client for authenticated requests, org management, and verification.
341
+ Access the GitHat API client for authenticated requests, org management, password reset, email verification, and more.
260
342
 
261
343
  ```tsx
262
344
  const {
263
- fetch, // <T>(path: string, init?: RequestInit) => Promise<T>
264
- getUserOrgs, // () => Promise<{ orgs: GitHatOrg[] }>
265
- verifyMCP, // (domain: string) => Promise<{ verified: boolean }>
266
- verifyAgent, // (wallet: string) => Promise<{ verified: boolean }>
345
+ // API Client
346
+ fetch, // <T>(path: string, init?: RequestInit) => Promise<T>
347
+ getUserOrgs, // () => Promise<{ orgs: GitHatOrg[] }>
348
+
349
+ // MCP & Agent Verification
350
+ verifyMCP, // (domain: string) => Promise<{ verified: boolean }>
351
+ verifyAgent, // (wallet: string) => Promise<{ verified: boolean }>
352
+
353
+ // Organization Metadata
354
+ getOrgMetadata, // () => Promise<OrgMetadata>
355
+ updateOrgMetadata, // (updates: OrgMetadata) => Promise<OrgMetadata>
356
+
357
+ // Password Management
358
+ forgotPassword, // (email: string) => Promise<{ success: boolean }>
359
+ resetPassword, // (token: string, newPassword: string) => Promise<{ success: boolean }>
360
+ changePassword, // (currentPassword: string, newPassword: string) => Promise<{ success: boolean }>
361
+
362
+ // Email Verification
363
+ verifyEmail, // (token: string) => Promise<{ success: boolean }>
364
+ resendVerificationEmail, // (email: string) => Promise<{ success: boolean }>
267
365
  } = useGitHat();
268
366
  ```
269
367
 
@@ -303,6 +401,143 @@ const { fetch } = useGitHat();
303
401
  const apps = await fetch<{ apps: App[] }>('/user/apps');
304
402
  ```
305
403
 
404
+ #### Example: Organization metadata
405
+
406
+ ```tsx
407
+ const { getOrgMetadata, updateOrgMetadata } = useGitHat();
408
+
409
+ // Read metadata
410
+ const meta = await getOrgMetadata();
411
+ console.log(meta.stripeAccountId);
412
+
413
+ // Update metadata (requires admin or owner role)
414
+ await updateOrgMetadata({ stripeAccountId: 'acct_xxx', features: ['pos'] });
415
+
416
+ // Delete a key by setting it to null
417
+ await updateOrgMetadata({ oldKey: null });
418
+ ```
419
+
420
+ #### Example: Password reset flow (programmatic)
421
+
422
+ ```tsx
423
+ const { forgotPassword, resetPassword, changePassword } = useGitHat();
424
+
425
+ // Request reset email
426
+ await forgotPassword('user@example.com');
427
+
428
+ // Reset with token (from email link)
429
+ await resetPassword(token, 'NewSecurePassword123!');
430
+
431
+ // Change password (authenticated user)
432
+ await changePassword('currentPassword', 'newSecurePassword123!');
433
+ ```
434
+
435
+ #### Example: Email verification (programmatic)
436
+
437
+ ```tsx
438
+ const { verifyEmail, resendVerificationEmail } = useGitHat();
439
+
440
+ // Verify with token from email link
441
+ await verifyEmail(token);
442
+
443
+ // Resend verification email
444
+ await resendVerificationEmail('user@example.com');
445
+ ```
446
+
447
+ ### `useData()`
448
+
449
+ Access GitHat's Customer Data API for storing app data in managed DynamoDB. Must be used within a `<GitHatProvider>`.
450
+
451
+ ```tsx
452
+ const {
453
+ put, // <T>(collection: string, data: T) => Promise<PutResult<T>>
454
+ get, // <T>(collection: string, id: string) => Promise<T | null>
455
+ query, // <T>(collection: string, options?: QueryOptions) => Promise<QueryResult<T>>
456
+ remove, // (collection: string, id: string) => Promise<DeleteResult>
457
+ batch, // (collection: string, operations: BatchOperation[]) => Promise<BatchResult>
458
+ } = useData();
459
+ ```
460
+
461
+ #### Example: CRUD operations
462
+
463
+ ```tsx
464
+ 'use client';
465
+
466
+ import { useData } from '@githat/nextjs';
467
+
468
+ interface Order {
469
+ id: string;
470
+ amount: number;
471
+ status: 'pending' | 'completed' | 'cancelled';
472
+ createdAt: string;
473
+ }
474
+
475
+ export function OrderManager() {
476
+ const { put, get, query, remove, batch } = useData();
477
+
478
+ // Create or update an item
479
+ const createOrder = async () => {
480
+ const result = await put<Order>('orders', {
481
+ id: 'order_123',
482
+ amount: 99.99,
483
+ status: 'pending',
484
+ createdAt: new Date().toISOString(),
485
+ });
486
+ console.log(result.created ? 'Created' : 'Updated');
487
+ };
488
+
489
+ // Get a single item
490
+ const getOrder = async () => {
491
+ const order = await get<Order>('orders', 'order_123');
492
+ if (order) {
493
+ console.log(`Order: $${order.amount}`);
494
+ }
495
+ };
496
+
497
+ // Query with filters and pagination
498
+ const getPendingOrders = async () => {
499
+ const { items, nextCursor } = await query<Order>('orders', {
500
+ filter: { status: 'pending' },
501
+ limit: 20,
502
+ });
503
+ console.log(`Found ${items.length} pending orders`);
504
+ };
505
+
506
+ // Delete an item
507
+ const deleteOrder = async () => {
508
+ await remove('orders', 'order_123');
509
+ };
510
+
511
+ // Batch operations (max 100 per request)
512
+ const bulkUpdate = async () => {
513
+ await batch('orders', [
514
+ { type: 'put', id: 'order_1', data: { amount: 50, status: 'completed' } },
515
+ { type: 'put', id: 'order_2', data: { amount: 75, status: 'pending' } },
516
+ { type: 'delete', id: 'order_3' },
517
+ ]);
518
+ };
519
+
520
+ return (
521
+ <div>
522
+ <button onClick={createOrder}>Create Order</button>
523
+ <button onClick={getOrder}>Get Order</button>
524
+ <button onClick={getPendingOrders}>List Pending</button>
525
+ <button onClick={deleteOrder}>Delete Order</button>
526
+ <button onClick={bulkUpdate}>Bulk Update</button>
527
+ </div>
528
+ );
529
+ }
530
+ ```
531
+
532
+ #### Data API Tier Limits
533
+
534
+ | Tier | Max Items |
535
+ |------------|-----------|
536
+ | Free | 1,000 |
537
+ | Basic | 10,000 |
538
+ | Pro | 100,000 |
539
+ | Enterprise | Unlimited |
540
+
306
541
  ---
307
542
 
308
543
  ## Middleware
@@ -310,13 +545,13 @@ const apps = await fetch<{ apps: App[] }>('/user/apps');
310
545
  The `authMiddleware` function protects your Next.js routes at the edge. Unauthenticated users are redirected to your sign-in page with a `redirect_url` query parameter for post-login redirect.
311
546
 
312
547
  ```ts
313
- // middleware.ts
548
+ // middleware.ts (Next.js 14-15)
314
549
  import { authMiddleware } from '@githat/nextjs/middleware';
315
550
 
316
551
  export default authMiddleware({
317
552
  publicRoutes: ['/', '/sign-in', '/sign-up', '/pricing'],
318
- signInUrl: '/sign-in', // default: '/sign-in'
319
- tokenKey: 'githat_access_token', // default: 'githat_access_token'
553
+ signInUrl: '/sign-in',
554
+ injectHeaders: true, // Optional: inject x-githat-* headers
320
555
  });
321
556
 
322
557
  export const config = {
@@ -326,13 +561,193 @@ export const config = {
326
561
 
327
562
  ### Middleware Options
328
563
 
329
- | Option | Type | Default | Description |
330
- |----------------|------------|-------------------------|------------------------------------------|
331
- | `publicRoutes` | `string[]` | `['/']` | Routes accessible without authentication |
332
- | `signInUrl` | `string` | `'/sign-in'` | Where to redirect unauthenticated users |
333
- | `tokenKey` | `string` | `'githat_access_token'` | Cookie name to check for the auth token |
564
+ | Option | Type | Default | Description |
565
+ |--------------------|------------|-------------------------|------------------------------------------------------|
566
+ | `publicRoutes` | `string[]` | `['/']` | Routes accessible without authentication |
567
+ | `signInUrl` | `string` | `'/sign-in'` | Where to redirect unauthenticated users |
568
+ | `tokenCookie` | `string` | `'githat_access'` | Cookie name for httpOnly access token |
569
+ | `legacyTokenCookie`| `string` | `'githat_access_token'` | Legacy cookie name (localStorage bridge) |
570
+ | `injectHeaders` | `boolean` | `false` | Inject `x-githat-*` headers into requests |
571
+ | `secretKey` | `string` | — | Secret key for local JWT verification (recommended) |
572
+
573
+ ### Header Injection
574
+
575
+ When `injectHeaders: true`, the middleware decodes the JWT and adds these headers to downstream requests:
576
+
577
+ | Header | Value |
578
+ |---------------------|----------------------------|
579
+ | `x-githat-user-id` | User's unique ID |
580
+ | `x-githat-email` | User's email address |
581
+ | `x-githat-org-id` | Current organization ID |
582
+ | `x-githat-org-slug` | Current organization slug |
583
+ | `x-githat-role` | User's role (owner/admin/member) |
584
+
585
+ This allows API routes to access user info without re-verifying the token.
334
586
 
335
- > **Note:** Since tokens are stored in `localStorage` on the client, middleware performs a cookie presence check. Full token validation happens server-side on API calls. Use the optional cookie bridge pattern to sync `localStorage` tokens to cookies for edge protection.
587
+ > **⚠️ Important: API routes are NOT protected by the middleware/proxy.** Both `authMiddleware` and `authProxy` automatically skip all `/api` routes (`pathname.startsWith('/api')` → pass-through). This is by design API routes should use `withAuth()` or `getAuth()` from `@githat/nextjs/server` to verify tokens. See [Server-Side Authentication](#server-side-authentication) below.
588
+
589
+ ---
590
+
591
+ ## Next.js 16+ Proxy
592
+
593
+ Next.js 16 renamed `middleware.ts` to `proxy.ts` and the export from `export default middleware` to `export const proxy`. Use `authProxy` for Next.js 16+:
594
+
595
+ ```ts
596
+ // proxy.ts (Next.js 16+)
597
+ import { authProxy } from '@githat/nextjs/proxy';
598
+
599
+ export const proxy = authProxy({
600
+ publicRoutes: ['/', '/about', '/pricing', '/sign-in', '/sign-up'],
601
+ signInUrl: '/sign-in',
602
+ injectHeaders: true,
603
+ secretKey: process.env.GITHAT_SECRET_KEY, // Recommended
604
+ });
605
+
606
+ export const config = {
607
+ matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
608
+ };
609
+ ```
610
+
611
+ The `authProxy` function accepts the same options as `authMiddleware`.
612
+
613
+ > **⚠️ Important: API routes are NOT protected by `authProxy` or `authMiddleware`.** Both automatically skip all `/api` routes. You **must** use `withAuth()` or `getAuth()` from `@githat/nextjs/server` to protect your API route handlers. See below.
614
+
615
+ ---
616
+
617
+ ## Server-Side Authentication
618
+
619
+ The `@githat/nextjs/server` module provides utilities for server-side token verification in API routes and middleware.
620
+
621
+ ### Import
622
+
623
+ ```ts
624
+ import {
625
+ verifyToken,
626
+ getAuth,
627
+ withAuth,
628
+ getOrgMetadata,
629
+ updateOrgMetadata,
630
+ COOKIE_NAMES,
631
+ type AuthPayload,
632
+ type VerifyOptions,
633
+ } from '@githat/nextjs/server';
634
+ ```
635
+
636
+ ### `verifyToken(token, options?)`
637
+
638
+ Verify a JWT token and return the decoded auth payload.
639
+
640
+ ```ts
641
+ // Local verification (fast, ~1ms) — recommended for production
642
+ const auth = await verifyToken(token, {
643
+ secretKey: process.env.GITHAT_SECRET_KEY,
644
+ });
645
+
646
+ // API-based verification (simpler setup, ~50-100ms)
647
+ const auth = await verifyToken(token);
648
+ ```
649
+
650
+ Returns `AuthPayload`:
651
+
652
+ ```ts
653
+ interface AuthPayload {
654
+ userId: string;
655
+ email: string;
656
+ orgId: string | null;
657
+ orgSlug: string | null;
658
+ role: 'owner' | 'admin' | 'member' | null;
659
+ tier: 'free' | 'basic' | 'pro' | 'enterprise' | null;
660
+ }
661
+ ```
662
+
663
+ ### `getAuth(request, options?)`
664
+
665
+ Extract and verify the auth token from a Next.js request. Checks cookies first, then Authorization header.
666
+
667
+ ```ts
668
+ // app/api/orders/route.ts
669
+ import { getAuth } from '@githat/nextjs/server';
670
+
671
+ export async function GET(request: Request) {
672
+ const auth = await getAuth(request, {
673
+ secretKey: process.env.GITHAT_SECRET_KEY,
674
+ });
675
+
676
+ if (!auth) {
677
+ return Response.json({ error: 'Unauthorized' }, { status: 401 });
678
+ }
679
+
680
+ // auth.userId, auth.orgId, auth.role available
681
+ return Response.json({ userId: auth.userId });
682
+ }
683
+ ```
684
+
685
+ ### `withAuth(handler, options?)`
686
+
687
+ Wrap an API route handler with authentication. The handler only runs if the request has a valid token. **This is required for every protected API route** — the middleware/proxy does not protect `/api` routes.
688
+
689
+ ```ts
690
+ // app/api/orders/route.ts
691
+ import { withAuth } from '@githat/nextjs/server';
692
+
693
+ export const GET = withAuth(
694
+ async (request, auth) => {
695
+ // auth is guaranteed to be valid here
696
+ const orders = await db.orders.findMany({
697
+ where: { orgId: auth.orgId },
698
+ });
699
+ return Response.json({ orders });
700
+ },
701
+ { secretKey: process.env.GITHAT_SECRET_KEY }
702
+ );
703
+ ```
704
+
705
+ #### Custom unauthorized response
706
+
707
+ ```ts
708
+ export const GET = withAuth(
709
+ async (request, auth) => {
710
+ return Response.json({ userId: auth.userId });
711
+ },
712
+ {
713
+ secretKey: process.env.GITHAT_SECRET_KEY,
714
+ onUnauthorized: () => Response.redirect('/sign-in'),
715
+ }
716
+ );
717
+ ```
718
+
719
+ ### `getOrgMetadata(orgId, options)` / `updateOrgMetadata(orgId, metadata, options)`
720
+
721
+ Server-side org metadata operations.
722
+
723
+ ```ts
724
+ import { getOrgMetadata, updateOrgMetadata } from '@githat/nextjs/server';
725
+
726
+ // Get metadata
727
+ const meta = await getOrgMetadata(orgId, {
728
+ token: accessToken,
729
+ apiUrl: 'https://api.githat.io',
730
+ });
731
+ console.log(meta.stripeAccountId);
732
+
733
+ // Update metadata
734
+ await updateOrgMetadata(
735
+ orgId,
736
+ { stripeAccountId: 'acct_xxx' },
737
+ { token: accessToken }
738
+ );
739
+ ```
740
+
741
+ ### `COOKIE_NAMES`
742
+
743
+ Constants for cookie names used by the SDK.
744
+
745
+ ```ts
746
+ import { COOKIE_NAMES } from '@githat/nextjs/server';
747
+
748
+ // COOKIE_NAMES.accessToken = 'githat_access'
749
+ // COOKIE_NAMES.refreshToken = 'githat_refresh'
750
+ ```
336
751
 
337
752
  ---
338
753
 
@@ -342,20 +757,36 @@ export const config = {
342
757
 
343
758
  Pass this to `<GitHatProvider config={...}>`:
344
759
 
345
- | Property | Type | Required | Default | Description |
346
- |-------------------|----------|----------|---------------------------|-----------------------------|
347
- | `publishableKey` | `string` | Yes | — | Your GitHat publishable key |
348
- | `apiUrl` | `string` | No | `'https://api.githat.io'` | API base URL |
349
- | `signInUrl` | `string` | No | `'/sign-in'` | Sign-in page route |
350
- | `signUpUrl` | `string` | No | `'/sign-up'` | Sign-up page route |
351
- | `afterSignInUrl` | `string` | No | `'/'` | Redirect after sign-in |
352
- | `afterSignOutUrl` | `string` | No | `'/'` | Redirect after sign-out |
760
+ | Property | Type | Required | Default | Description |
761
+ |------------------|--------------------------------|----------|---------------------------|--------------------------------------|
762
+ | `publishableKey` | `string` | Yes | — | Your GitHat publishable key |
763
+ | `apiUrl` | `string` | No | `'https://api.githat.io'` | API base URL |
764
+ | `signInUrl` | `string` | No | `'/sign-in'` | Sign-in page route |
765
+ | `signUpUrl` | `string` | No | `'/sign-up'` | Sign-up page route |
766
+ | `afterSignInUrl` | `string` | No | `'/'` | Redirect after sign-in |
767
+ | `afterSignOutUrl`| `string` | No | `'/'` | Redirect after sign-out |
768
+ | `tokenStorage` | `'localStorage' \| 'cookie'` | No | `'localStorage'` | Token storage mode (see below) |
769
+
770
+ ### Token Storage Modes
771
+
772
+ | Mode | Description |
773
+ |----------------|--------------------------------------------------------------------------|
774
+ | `localStorage` | Default. Tokens stored in browser localStorage. Simple client-side setup. |
775
+ | `cookie` | Tokens stored in httpOnly cookies. More secure, XSS-resistant. Better for SSR. |
776
+
777
+ When using `cookie` mode:
778
+
779
+ - Login/refresh automatically set httpOnly cookies
780
+ - SDK reads auth state from cookies server-side
781
+ - Better security for apps with server-side rendering
782
+ - Required for `getAuth()` and `withAuth()` server utilities
353
783
 
354
784
  ### Environment Variables
355
785
 
356
786
  ```env
357
787
  # .env.local
358
- NEXT_PUBLIC_GITHAT_KEY=pk_live_xxxxxxxxxxxxx
788
+ NEXT_PUBLIC_GITHAT_KEY=pk_live_your_key_here
789
+ GITHAT_SECRET_KEY=sk_live_your_secret_key # For server-side verification
359
790
  ```
360
791
 
361
792
  ### React/Vite Setup
@@ -375,7 +806,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
375
806
  );
376
807
  ```
377
808
 
378
- > **Note:** The middleware export (`@githat/nextjs/middleware`) is Next.js-specific. For Vite/CRA apps, use `<ProtectedRoute>` for client-side route protection.
809
+ > **Note:** The middleware/proxy exports are Next.js-specific. For Vite/CRA apps, use `<ProtectedRoute>` for client-side route protection.
379
810
 
380
811
  ---
381
812
 
@@ -387,7 +818,7 @@ Import the built-in dark theme:
387
818
  import '@githat/nextjs/styles';
388
819
  ```
389
820
 
390
- This provides styled defaults for all components (`SignInForm`, `SignUpForm`, `UserButton`, `OrgSwitcher`, etc.). All class names are prefixed with `githat-` to avoid conflicts.
821
+ This provides styled defaults for all components (`SignInForm`, `SignUpForm`, `ForgotPasswordForm`, `ResetPasswordForm`, `VerifyEmailStatus`, `ChangePasswordForm`, `UserButton`, `OrgSwitcher`, etc.). All class names are prefixed with `githat-` to avoid conflicts.
391
822
 
392
823
  ### Customization
393
824
 
@@ -417,6 +848,7 @@ Every export is fully typed. Import types directly:
417
848
 
418
849
  ```tsx
419
850
  import type {
851
+ // Core types
420
852
  GitHatUser,
421
853
  GitHatOrg,
422
854
  GitHatConfig,
@@ -425,6 +857,30 @@ import type {
425
857
  SignUpData,
426
858
  SignUpResult,
427
859
  GitHatContextValue,
860
+
861
+ // Password & Email
862
+ PasswordResetResult,
863
+ EmailVerificationResult,
864
+
865
+ // Server-side
866
+ AuthPayload,
867
+ VerifyOptions,
868
+ OrgMetadata,
869
+ WithAuthOptions,
870
+ AuthenticatedHandler,
871
+
872
+ // Customer Data API
873
+ DataItem,
874
+ QueryOptions,
875
+ QueryResult,
876
+ PutResult,
877
+ DeleteResult,
878
+ BatchOperation,
879
+ BatchResult,
880
+
881
+ // Middleware/Proxy
882
+ AuthHandlerOptions,
883
+ AuthProxyOptions,
428
884
  } from '@githat/nextjs';
429
885
  ```
430
886
 
@@ -447,6 +903,15 @@ interface GitHatOrg {
447
903
  tier: string;
448
904
  }
449
905
 
906
+ interface AuthPayload {
907
+ userId: string;
908
+ email: string;
909
+ orgId: string | null;
910
+ orgSlug: string | null;
911
+ role: 'owner' | 'admin' | 'member' | null;
912
+ tier: 'free' | 'basic' | 'pro' | 'enterprise' | null;
913
+ }
914
+
450
915
  interface SignUpData {
451
916
  email: string;
452
917
  password: string;
@@ -470,7 +935,77 @@ Use the companion CLI to scaffold a complete Next.js or React app with GitHat au
470
935
  npx create-githat-app my-app
471
936
  ```
472
937
 
473
- This generates a full project connected to GitHat's hosted backend — sign-in, sign-up, dashboard, settings, and team management pages. No backend to deploy.
938
+ This generates a full project connected to GitHat's hosted backend — sign-in, sign-up, password reset, email verification, dashboard, settings, and team management pages. No backend to deploy.
939
+
940
+ ---
941
+
942
+ ## Known Limitations
943
+
944
+ These are things GitHat does **not** provide today. They are not blockers — each has a recommended workaround.
945
+
946
+ ### No webhooks / event system
947
+
948
+ GitHat does not send server-side notifications when users register, verify email, or delete accounts. Use `onSuccess` callbacks on form components to call your own API endpoint instead:
949
+
950
+ ```tsx
951
+ <SignUpForm
952
+ onSuccess={async (result) => {
953
+ // result: { requiresVerification: boolean, email: string }
954
+ // Call your API to sync user, send welcome email, etc.
955
+ await fetch('/api/on-signup', {
956
+ method: 'POST',
957
+ headers: { 'Content-Type': 'application/json' },
958
+ body: JSON.stringify({ email: result.email }),
959
+ });
960
+ }}
961
+ />
962
+
963
+ <VerifyEmailStatus
964
+ token={token}
965
+ onSuccess={async () => {
966
+ // Email verified — trigger your post-verification logic
967
+ await fetch('/api/on-email-verified', { method: 'POST' });
968
+ }}
969
+ />
970
+ ```
971
+
972
+ ### No user metadata API
973
+
974
+ Org metadata exists (`getOrgMetadata` / `updateOrgMetadata`), but there is no per-user equivalent. Use the [Customer Data API](https://githat.io/docs/data) or your own database:
975
+
976
+ ```tsx
977
+ import { useData, useAuth } from '@githat/nextjs';
978
+
979
+ function useUserPreferences() {
980
+ const { user } = useAuth();
981
+ const { put, get } = useData();
982
+
983
+ const getPreferences = async () => {
984
+ if (!user) return null;
985
+ return get('user-preferences', user.id);
986
+ };
987
+
988
+ const updatePreferences = async (prefs: Partial<Preferences>) => {
989
+ if (!user) throw new Error('Not authenticated');
990
+ const current = await getPreferences() || { theme: 'dark', notifications: true };
991
+ return put('user-preferences', { id: user.id, ...current, ...prefs });
992
+ };
993
+
994
+ return { getPreferences, updatePreferences };
995
+ }
996
+ ```
997
+
998
+ ### No bulk user import
999
+
1000
+ There is no self-service API to import existing users from another auth provider. For beta or new apps, users re-register. For production migrations, contact us for enterprise import.
1001
+
1002
+ ### API routes not protected by middleware
1003
+
1004
+ `authMiddleware` and `authProxy` skip `/api` routes by design. Use `withAuth()` or `getAuth()` per-route — see [Server-Side Authentication](#server-side-authentication).
1005
+
1006
+ ### Migrating from Clerk?
1007
+
1008
+ See the [Clerk Migration Guide](https://githat.io/docs/clerk-migration) for a step-by-step walkthrough of switching from Clerk to GitHat with zero downtime.
474
1009
 
475
1010
  ---
476
1011