@githat/nextjs 0.2.0 → 0.2.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
@@ -2,16 +2,16 @@
2
2
 
3
3
  # @githat/nextjs
4
4
 
5
- **Drop-in authentication for Next.js and React apps.**
5
+ **SDK for connecting your Next.js or React app to GitHat's identity platform — auth, orgs, teams, API keys, and more.**
6
6
 
7
- Build secure, multi-tenant applications with pre-built UI components, hooks, and middleware in under 5 minutes.
7
+ GitHat is your backend. This SDK connects your app to `api.githat.io` with pre-built UI components, hooks, and middleware. No backend to deploy.
8
8
 
9
9
  [![npm version](https://img.shields.io/npm/v/@githat/nextjs)](https://www.npmjs.com/package/@githat/nextjs)
10
10
  [![npm downloads](https://img.shields.io/npm/dm/@githat/nextjs)](https://www.npmjs.com/package/@githat/nextjs)
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,14 +19,17 @@ Build secure, multi-tenant applications with pre-built UI components, hooks, and
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
- - **Multi-Tenant** — Built-in organization switching with role-based access
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
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
28
30
  - **TypeScript First** — Full type definitions for every component, hook, and utility
29
31
  - **Dual Output** — Ships ESM + CJS so it works everywhere
32
+ - **Hosted Backend** — All features powered by GitHat's platform (`api.githat.io`). No backend deployment required
30
33
 
31
34
  ---
32
35
 
@@ -98,6 +101,10 @@ export default function SignUpPage() {
98
101
 
99
102
  ### 5. Protect Routes with Middleware
100
103
 
104
+ <!-- tabs:start -->
105
+
106
+ #### **Next.js 14-15 (middleware.ts)**
107
+
101
108
  ```ts
102
109
  // middleware.ts (project root)
103
110
  import { authMiddleware } from '@githat/nextjs/middleware';
@@ -111,6 +118,23 @@ export const config = {
111
118
  };
112
119
  ```
113
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
+
114
138
  ### 6. Use Auth State Anywhere
115
139
 
116
140
  ```tsx
@@ -141,32 +165,91 @@ That's it. You now have a fully authenticated app with sign-in, sign-up, route p
141
165
 
142
166
  ### Provider
143
167
 
144
- | Component | Description | Key Props |
145
- |------------------|------------------------------------------|---------------------------------------------------------|
168
+ | Component | Description | Key Props |
169
+ |------------------|------------------------------------------|-----------------------------------------------------------|
146
170
  | `GitHatProvider` | Wraps your app and provides auth context | `config` (required) — see [Configuration](#configuration) |
147
171
 
148
172
  ### Authentication Forms
149
173
 
150
- | Component | Description | Props |
151
- |--------------|--------------------------------------|--------------------------------------------------|
152
- | `SignInForm` | Email + password sign-in form | `onSuccess?`, `signUpUrl?`, `forgotPasswordUrl?` |
153
- | `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?` |
154
182
 
155
183
  ### Navigation
156
184
 
157
- | Component | Description | Props |
158
- |----------------|----------------------------------------------|------------------------------------------|
159
- | `SignInButton` | Link/button to your sign-in page | `className?`, `children?`, `href?` |
160
- | `SignUpButton` | Link/button to your sign-up page | `className?`, `children?`, `href?` |
161
- | `UserButton` | Avatar dropdown with user info and sign-out | — (uses context) |
162
- | `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) |
163
191
 
164
192
  ### Protection & Verification
165
193
 
166
- | Component | Description | Props |
167
- |------------------|--------------------------------------------|--------------------------------------------------------------|
168
- | `ProtectedRoute` | Redirects unauthenticated users to sign-in | `children`, `fallback?` |
169
- | `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
+ ```
170
253
 
171
254
  ### Example: Protected Dashboard
172
255
 
@@ -255,14 +338,30 @@ export function CustomLogin() {
255
338
 
256
339
  ### `useGitHat()`
257
340
 
258
- 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.
259
342
 
260
343
  ```tsx
261
344
  const {
262
- fetch, // <T>(path: string, init?: RequestInit) => Promise<T>
263
- getUserOrgs, // () => Promise<{ orgs: GitHatOrg[] }>
264
- verifyMCP, // (domain: string) => Promise<{ verified: boolean }>
265
- 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 }>
266
365
  } = useGitHat();
267
366
  ```
268
367
 
@@ -302,6 +401,143 @@ const { fetch } = useGitHat();
302
401
  const apps = await fetch<{ apps: App[] }>('/user/apps');
303
402
  ```
304
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
+
305
541
  ---
306
542
 
307
543
  ## Middleware
@@ -309,13 +545,13 @@ const apps = await fetch<{ apps: App[] }>('/user/apps');
309
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.
310
546
 
311
547
  ```ts
312
- // middleware.ts
548
+ // middleware.ts (Next.js 14-15)
313
549
  import { authMiddleware } from '@githat/nextjs/middleware';
314
550
 
315
551
  export default authMiddleware({
316
552
  publicRoutes: ['/', '/sign-in', '/sign-up', '/pricing'],
317
- signInUrl: '/sign-in', // default: '/sign-in'
318
- tokenKey: 'githat_access_token', // default: 'githat_access_token'
553
+ signInUrl: '/sign-in',
554
+ injectHeaders: true, // Optional: inject x-githat-* headers
319
555
  });
320
556
 
321
557
  export const config = {
@@ -325,13 +561,193 @@ export const config = {
325
561
 
326
562
  ### Middleware Options
327
563
 
328
- | Option | Type | Default | Description |
329
- |----------------|------------|-------------------------|------------------------------------------|
330
- | `publicRoutes` | `string[]` | `['/']` | Routes accessible without authentication |
331
- | `signInUrl` | `string` | `'/sign-in'` | Where to redirect unauthenticated users |
332
- | `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.
333
586
 
334
- > **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
+ ```
335
751
 
336
752
  ---
337
753
 
@@ -341,20 +757,36 @@ export const config = {
341
757
 
342
758
  Pass this to `<GitHatProvider config={...}>`:
343
759
 
344
- | Property | Type | Required | Default | Description |
345
- |-------------------|----------|----------|---------------------------|-----------------------------|
346
- | `publishableKey` | `string` | Yes | — | Your GitHat publishable key |
347
- | `apiUrl` | `string` | No | `'https://api.githat.io'` | API base URL |
348
- | `signInUrl` | `string` | No | `'/sign-in'` | Sign-in page route |
349
- | `signUpUrl` | `string` | No | `'/sign-up'` | Sign-up page route |
350
- | `afterSignInUrl` | `string` | No | `'/'` | Redirect after sign-in |
351
- | `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
352
783
 
353
784
  ### Environment Variables
354
785
 
355
786
  ```env
356
787
  # .env.local
357
- 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
358
790
  ```
359
791
 
360
792
  ### React/Vite Setup
@@ -374,7 +806,7 @@ ReactDOM.createRoot(document.getElementById('root')!).render(
374
806
  );
375
807
  ```
376
808
 
377
- > **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.
378
810
 
379
811
  ---
380
812
 
@@ -386,7 +818,7 @@ Import the built-in dark theme:
386
818
  import '@githat/nextjs/styles';
387
819
  ```
388
820
 
389
- 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.
390
822
 
391
823
  ### Customization
392
824
 
@@ -416,6 +848,7 @@ Every export is fully typed. Import types directly:
416
848
 
417
849
  ```tsx
418
850
  import type {
851
+ // Core types
419
852
  GitHatUser,
420
853
  GitHatOrg,
421
854
  GitHatConfig,
@@ -424,6 +857,30 @@ import type {
424
857
  SignUpData,
425
858
  SignUpResult,
426
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,
427
884
  } from '@githat/nextjs';
428
885
  ```
429
886
 
@@ -446,6 +903,15 @@ interface GitHatOrg {
446
903
  tier: string;
447
904
  }
448
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
+
449
915
  interface SignUpData {
450
916
  email: string;
451
917
  password: string;
@@ -469,7 +935,77 @@ Use the companion CLI to scaffold a complete Next.js or React app with GitHat au
469
935
  npx create-githat-app my-app
470
936
  ```
471
937
 
472
- This generates a full project with sign-in, sign-up, dashboard, settings, and team management pages all wired up and ready 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.
473
1009
 
474
1010
  ---
475
1011