Skip to main content
GitHat

@githat/nextjs SDK Reference

Complete developer reference for @githat/nextjs v0.21.2 — components, hooks, server utilities, middleware, and config, sourced from the SDK source tree.

Package overview

Package name: @githat/nextjs. Version: 0.21.2. License: Proprietary. Published to the public npm registry.

Peer dependencies: next >=14.0.0, react >=18.0.0, react-dom >=18.0.0. Runtime dependencies: @simplewebauthn/browser ^11.0.0, jose ^5.2.0.

The package ships four named entry points: the main entry (@githat/nextjs) for client-side React, @githat/nextjs/server for server-side utilities, @githat/nextjs/middleware for Next.js 14/15 middleware, and @githat/nextjs/proxy for Next.js 16+ proxy. A CSS entry (@githat/nextjs/styles, which resolves to dist/githat.css) ships the default component styles.

Token verification is RS256 only, verified against the public JWKS at https://api.githat.io/.well-known/jwks.json. The SDK caches the JWKS for 5 minutes per process. HS256/secretKey paths were removed in v0.16.

Installation

Install the package and import the default stylesheet once in your root layout.

npm install @githat/nextjs

GitHatProvider — client component

GitHatProvider is the React context root. Mount it once, high in the tree (e.g. app/layout.tsx). All hooks and components require it as an ancestor.

Props: a single `config` prop of type GitHatConfig (see Config reference below) and standard `children: React.ReactNode`.

The provider validates the stored session on mount by calling /auth/me through the configured apiUrl. It emits the `githat:auth-changed` CustomEvent on sign-in/out so sibling components can react without prop-drilling. Cross-tab sign-out is propagated via BroadcastChannel with a localStorage storage-event fallback.

In dev mode (no publishable key, or key not starting with pk_live_/pk_test_) the provider renders a dismissable banner on localhost and logs a console.warn. On any non-localhost origin without a key, it logs console.error.

The default tokenStorage is 'cookie'. When cookie mode is used, all auth requests must go through a same-origin proxy that re-emits the upstream Set-Cookie (see githatApiProxy below). Do not set apiUrl to api.githat.io directly in production cookie mode.

// app/layout.tsx
import { GitHatProvider } from '@githat/nextjs';
import '@githat/nextjs/styles';

export default function RootLayout({ children }: { children: React.ReactNode }) {
  return (
    <html lang="en">
      <body>
        <GitHatProvider
          config={{
            publishableKey: process.env.NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY!,
            apiUrl: '/api/githat',           // same-origin proxy (required in cookie mode)
            appName: 'My App',               // shown on auth forms
            appId: process.env.NEXT_PUBLIC_GITHAT_APP_ID,
            afterSignInUrl: '/dashboard',
            afterSignOutUrl: '/',
            tokenStorage: 'cookie',           // default; 'localStorage' also accepted
            captchaSiteKey: process.env.NEXT_PUBLIC_TURNSTILE_SITE_KEY,
          }}
        >
          {children}
        </GitHatProvider>
      </body>
    </html>
  );
}

GitHatConfig reference

All fields are on the GitHatConfig interface, passed as the `config` prop to GitHatProvider.

publishableKey (string, required): your app's publishable key from the GitHat dashboard. Keys starting with pk_live_ are production; pk_test_ are test. Any other value triggers dev-mode warnings.

appId (string, optional): the GitHat-Apps record UUID (e.g. 'd10c6fb5-00dd-40fe-9ecb-320d3808cd5e'). Required for app-scoped hooks such as useEmailDomains. Also passed as the expected `aud` in verifyToken calls.

appName (string, optional): display name shown on auth form headings — 'Sign in to {appName}', 'Create your {appName} account'. Omit for generic headings.

apiUrl (string, optional, default: 'https://api.githat.io'): GitHat API base URL. In production with cookie mode you MUST set this to a same-origin proxy path (e.g. '/api/githat') so Set-Cookie headers are issued on your domain.

signInUrl (string, default: '/sign-in'): path where unauthenticated users are redirected by the proxy/middleware.

signUpUrl (string, default: '/sign-up'): URL linked from sign-in forms.

afterSignInUrl (string, default: '/dashboard'): where to redirect after a successful sign-in when no redirect_url query param is present.

afterSignOutUrl (string, default: '/'): where to redirect after signOut.

accountUrl (string, default: '/account/security'): URL for the UserButton's 'Account / Security' link.

tokenStorage ('cookie' | 'localStorage', default: 'cookie'): storage mode. 'cookie' stores tokens in httpOnly cookies via the same-origin proxy. 'localStorage' stores tokens in the browser (XSS-readable — only for static-host scenarios where httpOnly cookies are unavailable).

captchaSiteKey (string, optional): Cloudflare Turnstile site key. When set, SignInForm and SignUpForm automatically render the Turnstile widget when the auth-router replies 403 captcha_required (high-suspicion traffic only). Required to handle captcha challenges; without it, high-suspicion users see an error with no recovery path.

useAuth — core auth hook

Returns the full GitHatContextValue — the union of AuthState, AuthActions, and config. Throws if called outside a GitHatProvider.

AuthState fields: user (GitHatUser | null), org (GitHatOrg | null), isSignedIn (boolean), isLoading (boolean), authError (string | null).

AuthActions: signIn(email, password, options?) => Promise<SignInResult>, signUp(data: SignUpData) => Promise<SignUpResult>, signOut() => Promise<void>, switchOrg(orgId) => Promise<void>, completeSignIn(tokens: SignInTokens) => void.

signIn returns void on success or MfaChallengeRequired ({requiresMfa: true, mfaTicket, factors?, smsPhoneSuffix?}) when MFA is enabled. The caller must render MfaChallenge when requiresMfa is true.

signOut clears auth state locally first (before the network call) and broadcasts to other tabs, then POSTs to /auth/logout.

'use client';
import { useAuth } from '@githat/nextjs';

export function AccountMenu() {
  const { user, isSignedIn, isLoading, signOut } = useAuth();

  if (isLoading) return <span>Loading...</span>;
  if (!isSignedIn) return <a href="/sign-in">Sign in</a>;

  return (
    <div>
      <span>{user?.name}</span>
      <button onClick={() => signOut()}>Sign out</button>
    </div>
  );
}

useGitHat — authenticated API hook

Extends useAuth with an authenticated fetch client and convenience methods for common API operations. All methods throw on error.

Returned methods:

fetch(path, init?): the raw fetchApi call — use for any endpoint not covered by the helpers.

getUserOrgs(): fetches GET /user/orgs, returns { orgs: GitHatOrg[] }.

verifyAgent(wallet): GET /verify/agent/{wallet}, returns { verified: boolean }.

getOrgMetadata(): GET /orgs/{org.id}/metadata, returns OrgMetadata (key-value Record). Requires org to be active.

updateOrgMetadata(updates): PATCH /orgs/{org.id}/metadata with a partial object. Set a key to null to delete it. Returns the merged OrgMetadata.

forgotPassword(email): POST /auth/forgot-password.

resetPassword(token, newPassword): POST /auth/reset-password.

changePassword(currentPassword, newPassword): POST /auth/change-password.

verifyEmail(token): POST /auth/verify-email.

resendVerificationEmail(email): POST /auth/resend-verification.

OAuth URL getters — getGitHubOAuthUrl(opts?), getGoogleOAuthUrl(opts?), getMicrosoftOAuthUrl(opts?), getFacebookOAuthUrl(opts?), getInstagramOAuthUrl(opts?), getTikTokOAuthUrl(opts?), getAppleOAuthUrl(opts?): each returns Promise<{ url: string }>. Optional opts: { redirectUri?, state? }.

OAuth code exchanges — signInWithGitHub(code, opts?), signInWithGoogle(code, opts?), signInWithMicrosoft(code, opts?), signInWithFacebook(code, opts?), signInWithInstagram(code, opts?), signInWithTikTok(code, opts?): all return Promise<{ user: GitHatUser, org: GitHatOrg | null, isNewUser: boolean }>.

signInWithApple(code, opts?): same shape, with additional opts.user (the JSON string Apple sends on first auth with firstName/lastName/email).

Note: signInWithGoogle checks tokenStorage from the context config. In cookie mode it calls /auth/oauth/google?setCookie=true so the backend sets httpOnly cookies.

'use client';
import { useGitHat } from '@githat/nextjs';

export function OrgSettings() {
  const { getOrgMetadata, updateOrgMetadata } = useGitHat();

  const handleSave = async () => {
    const meta = await getOrgMetadata();
    await updateOrgMetadata({ plan: 'pro', onboardedAt: new Date().toISOString() });
  };

  return <button onClick={handleSave}>Save</button>;
}

Clerk-compatible hooks

For apps migrating from @clerk/nextjs, the following hooks are exported from @githat/nextjs and return shapes that closely match Clerk's public API.

useUser(): returns { isSignedIn: boolean | undefined, isLoaded: boolean, user: ClerkCompatUser | null }. ClerkCompatUser extends GitHatUser with firstName, lastName, fullName, imageUrl (alias for avatarUrl), and primaryEmailAddress ({ emailAddress, verification: { status } }).

useAuthClerk(): Clerk-style useAuth(). Returns { isSignedIn, isLoaded, userId, sessionId (always null — GitHat does not expose session IDs client-side), orgId, orgRole, orgSlug, getToken(), signOut() }.

useSignIn(): thin facade. Returns { isLoaded, signIn: { create({identifier, password}) }, setActive (no-op) }. signIn.create returns { status: 'complete' | 'needs_mfa' | 'failed', createdSessionId, error? }.

useSignUp(): Returns { isLoaded, signUp: { create({emailAddress, password, firstName?, lastName?}), prepareEmailAddressVerification (no-op — email sent on /auth/register), attemptEmailAddressVerification({code}) }, setActive (no-op) }.

useClerk(): returns { signOut, openSignIn, openSignUp, redirectToSignIn, redirectToSignIn, redirectToUserProfile, user, session, __githat (the full useGitHat() return) }.

The canonical GitHat hook is useGitHat(). The Clerk-compat hooks exist to reduce churn when migrating; use useAuth/useGitHat in new code.

Specialised hooks

All hooks below are exported from the main @githat/nextjs entry and require GitHatProvider.

useMfa(): MFA enrollment and verification. Returns { setupTotp(), verifyTotp(code), enableTotp(code), disableTotp(code), setupSms(phone), verifySms(code), status() }. Relevant types: MfaSetupResult, MfaEnableResult, MfaVerifyOpts.

useMagicLink(): returns { request(email), verify(token) }. Relevant types: MagicLinkRequestResult, MagicLinkVerifyResult, MagicLinkMfaRequired.

usePasskey(): WebAuthn passkey management. Returns { register(name), signIn(email?), list(), remove(credentialId) }. Relevant types: PasskeyRecord, PasskeyRegisterResult, PasskeySignInResult.

useSessions(): list active sessions. Relevant types: Session, SessionsListResult.

useAuditLog(): access the account audit log. Relevant types: AuditEvent, AuditEventType, AuditLogListResult, AuditLogQueryOptions.

useStorage(): file storage. Relevant types: StorageObject, StorageUploadOptions, StorageUploadResult, StorageListOptions, StorageListResult.

useRealtime(): real-time subscriptions. Relevant types: RealtimeStatus, RealtimeMessage.

useDomains(): domain onboarding workflow. Relevant types include DomainOnboardingRecord, DomainVerificationStatus, DnsRecord, CertValidationRecord, OnboardResult, DomainCheckResult, DomainAvailability, RegisterDomainOptions, RegisterResult.

useDeployments(): deployment listing and log tailing. Relevant types: Deployment, DeploymentStatus, LogLine.

useAnalytics() / useOrgAnalytics(): analytics overview, time-series, and funnel data.

useObservability(): Lambda metrics, API Gateway metrics, recent failures, per-app deploy health.

useBilling(): plan status, usage, checkout link, portal link, cancel. Relevant types: BillingPlan, BillingPlanStatus, BillingLimits, BillingUsage, BillingStatus, CheckoutResult, PortalResult, CancelResult.

useAppBilling(): app-level billing (Connect-style). Relevant types: AppBillingStatus, ConnectLinkResult, LineItem, AppCheckoutResult, AppPortalResult.

useEmail(): transactional email send. Returns { send(options: SendEmailOptions) }.

useEmailDomains(): sender-domain management. Returns { setupEmailDomain(hostname), listEmailDomains(), getEmailDomainStatus(hostname), deleteEmailDomain(hostname) }. Requires appId in GitHatConfig.

useCaptcha(): returns { verifyCaptcha(turnstileToken) => Promise<{ captchaPass: string }> }. Used internally by SignInForm/SignUpForm; expose for standalone captcha flows.

UI components

All components are exported from the main @githat/nextjs entry and are client components ('use client').

Auth forms: SignInForm, SignUpForm, ForgotPasswordForm, ResetPasswordForm, VerifyEmailStatus, ChangePasswordForm.

SignInForm props: onSuccess?(), signUpUrl?, forgotPasswordUrl?, appName?, logo? (ReactNode), oauth? (OAuthProvider[], default: ['google','github','apple','microsoft','facebook','instagram','tiktok'] — pass [] to suppress), footer? ('githat' | 'none' | ReactNode, default: 'githat'), oauthRedirectBase?, showMagicLink? (boolean, default: true). OAuthProvider type: 'google' | 'apple' | 'microsoft' | 'facebook' | 'instagram' | 'tiktok' | 'github'.

MFA components: MfaSetupForm (props: MfaSetupFormProps), MfaChallenge (props: MfaChallengeProps — requires mfaTicket, factors?, smsPhoneSuffix?, onSuccess(result: MfaChallengeResult), onError(err)), MfaManager.

Buttons: SignInButton, SignUpButton, ContinueWithGitHat (props: ContinueWithGitHatProps — authBaseUrl, redirectTo?, label? (default 'Continue'), className?), UserButton, OrgSwitcher.

OAuth buttons: GitHubButton, GoogleButton, MicrosoftButton, FacebookButton, AppleButton, InstagramButton, TikTokButton. Each accepts: children?, redirectUri?, onError(err)?, className?, variant? ('default' | 'outline'), disabled?.

OAuth callbacks: GitHubCallback, GoogleCallback, MicrosoftCallback, FacebookCallback, AppleCallback, InstagramCallback, TikTokCallback. Each accepts: redirectUrl?, newUserRedirectUrl?, onSuccess({user, org, isNewUser})?, onError(err)?, loadingComponent?, errorComponent(error: string)?.

VerifiedBadge, ProtectedRoute, StorageDropzone (props: StorageDropzoneProps), MagicLinkVerify (props: MagicLinkVerifyProps), PasskeyButton (props: PasskeyButtonProps), TurnstileChallenge (props: TurnstileChallengeProps — siteKey, onToken(token), theme?, size?).

Dashboard components (GitHat-app-owner surfaces): DomainOnboardingWizard, DeploymentList, PricingTable, AnalyticsDashboard, ObservabilityPanel.

ContinueWithGitHat note: this component is designed for sibling fleet apps (white-label hosted auth redirect). The default label is 'Continue', never 'GitHat', because the component is meant to be invisible as an auth provider to end users. The label can be overridden with the `label` prop.

// app/sign-in/page.tsx
import { SignInForm } from '@githat/nextjs';

export default function SignInPage() {
  return (
    <SignInForm
      signUpUrl="/sign-up"
      forgotPasswordUrl="/forgot-password"
      oauth={['google', 'apple']}
      footer="none"
    />
  );
}

Server utilities — @githat/nextjs/server

All exports in this entry are server-side only. Do not import in client components.

verifyToken(token, options?): verifies an RS256 JWT against the JWKS at ${apiUrl}/.well-known/jwks.json and returns AuthPayload. Options: apiUrl (default 'https://api.githat.io'), audience (string | string[] — your appId UUID; omit to accept any aud), issuer (string | null — defaults to apiUrl; pass null only in tests). Throws on invalid/expired/wrong-aud/wrong-iss tokens. JWT must use RS256 (other alg values including 'none' are rejected at the header-peek step).

AuthPayload fields: userId (string), email (string), orgId (string | null), orgSlug (string | null), role ('owner' | 'admin' | 'member' | null), tier (always null — tier requires DB lookup), aud (string | null), appId (string | null).

getAuth(request, options?): extracts and verifies the token from a Request object. Checks the githat_access cookie first, then the Authorization: Bearer header. Returns AuthPayload | null (null = unauthenticated; throws on invalid token).

auth(options?): Server Component helper — reads the githat_access cookie via next/headers.cookies() and verifies it. Returns AuthPayload | null. Use in Server Components, layouts, and server actions where you have no Request object. Falls back to returning null in Edge runtime or non-Next environments.

currentUser(options?): wraps auth() and returns { id, email, orgId, orgRole, raw: AuthPayload } | null. Pure projection of the JWT — does NOT call /auth/me.

withAuth(handler, options?): wraps a Next.js App Router route handler. The inner handler receives (request, auth: AuthPayload). On no token: returns 401 JSON or the custom onUnauthorized response. Options: apiUrl, audience, onUnauthorized?.

getOrgMetadata(orgId, { token, apiUrl? }): GET /orgs/{orgId}/metadata server-side. Returns OrgMetadata.

updateOrgMetadata(orgId, metadata, { token, apiUrl? }): PATCH /orgs/{orgId}/metadata server-side. Returns merged OrgMetadata.

sendEmail(options: ServerSendEmailOptions, config: { token, apiUrl? }): POST /email/send server-side. Options: to, subject, html?, text?, replyTo?, from?. Returns ServerSendEmailResult { messageId, to, subject, sent }.

githatApiProxy(options?): builds a Next.js App Router catch-all route handler that proxies all requests to the GitHat API upstream and re-issues Set-Cookie headers on the consumer's domain. Options: apiUrl. Returns handlers for GET, POST, PUT, PATCH, DELETE, OPTIONS. Mount at /api/githat/[...path]/route.ts. Pair with apiUrl: '/api/githat' in GitHatProvider.

withGitHat(nextConfig, options?): Next.js config wrapper that injects the Content-Security-Policy header (connect-src https://api.githat.io plus dev localhost entries) and security headers (X-Content-Type-Options, X-Frame-Options, Referrer-Policy, HSTS in production). Options: apiUrl, extraConnectSrc, extraImgSrc, extraScriptSrc, extraStyleSrc, extraFontSrc, extraFrameSrc, extraFrameAncestors, reportUri, reportTo, csp (default true), securityHeaders (default true). In production, unsafe-inline is NOT added to script-src by default.

COOKIE_NAMES: exported constant with accessToken: 'githat_access' and refreshToken: 'githat_refresh'.

// app/api/orders/route.ts
import { withAuth } from '@githat/nextjs/server';

export const GET = withAuth(
  async (request, auth) => {
    // auth.userId, auth.orgId, auth.role available
    return Response.json({ userId: auth.userId });
  },
  { audience: process.env.GITHAT_APP_ID }
);

// app/dashboard/page.tsx (Server Component)
import { auth } from '@githat/nextjs/server';
import { redirect } from 'next/navigation';

export default async function Dashboard() {
  const session = await auth();
  if (!session) redirect('/sign-in');
  return <div>Hello {session.userId}</div>;
}

// src/app/api/githat/[...path]/route.ts
import { githatApiProxy } from '@githat/nextjs/server';
export const { GET, POST, PUT, PATCH, DELETE, OPTIONS } = githatApiProxy();

// next.config.ts
import { withGitHat } from '@githat/nextjs/server';
const nextConfig = { output: 'standalone' };
export default withGitHat(nextConfig);

Middleware / Proxy — route protection

The SDK exports two route-protection handlers for Next.js: authMiddleware from @githat/nextjs/middleware (Next.js 14/15) and authProxy from @githat/nextjs/proxy (Next.js 16+). Both wrap the shared handleAuthRequest function.

Both accept the same AuthHandlerOptions: publicRoutes (string[], default ['/']), signInUrl (string, default '/sign-in'), tokenCookie (string, default 'githat_access'), legacyTokenCookie (string, default 'githat_access_token'), injectHeaders (boolean, default false), apiUrl (string, default 'https://api.githat.io'), audience (string | string[]), verifyTokens (boolean, default false).

/api routes and paths starting with /_next or containing a '.' (static files) always pass through — they are never redirected to signInUrl.

When injectHeaders is true, the handler decodes the JWT and injects x-githat-user-id, x-githat-email, x-githat-org-id, x-githat-org-slug, x-githat-role into the request headers seen by downstream Server Components and API routes. Values containing non-Latin-1 characters are percent-encoded; consumers must call decodeURIComponent when reading them.

When verifyTokens is also true, the JWT is cryptographically verified via JWKS (RS256 only) before injection. Without verifyTokens, the payload is decoded without signature verification ('trust-the-cookie' mode). Use verifyTokens: true on any route that gates real authorization on the injected headers.

The redirect on unauthenticated requests appends redirect_url={pathname} to the signInUrl so the post-login redirect can restore the user's intended destination.

Note: as of Next.js 16, the file is named proxy.ts and the export is `export const proxy = authProxy(...)`. On Next.js 14/15 the file is middleware.ts and the export is `export default authMiddleware(...)`.

// proxy.ts — Next.js 16+
import { authProxy } from '@githat/nextjs/proxy';

export const proxy = authProxy({
  publicRoutes: ['/', '/about', '/pricing'],
  signInUrl: '/sign-in',
  injectHeaders: true,
  verifyTokens: true,
  audience: process.env.GITHAT_APP_ID,
});

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

// middleware.ts — Next.js 14/15
import { authMiddleware } from '@githat/nextjs/middleware';

export default authMiddleware({
  publicRoutes: ['/', '/about'],
  signInUrl: '/sign-in',
});

export const config = {
  matcher: ['/((?!_next/static|_next/image|favicon.ico).*)'],
};

GitHatUser and GitHatOrg types

GitHatUser: id (string), email (string | null), name (string), avatarUrl (string | null), emailVerified (boolean), mfaEnabled? (boolean — TOTP), mfaSmsEnabled? (boolean), mfaSmsPhone? (string | null — E.164 format), passkeys? (Array<{ credentialId, deviceName, createdAt, lastUsedAt }>), githubUsername?, googleId?, microsoftId?, facebookId?, appleId?, instagramId?, tiktokId?, authProvider? ('email' | 'github' | 'google' | 'microsoft' | 'facebook' | 'apple' | 'instagram' | 'tiktok').

GitHatOrg: id (string), name (string), slug (string), role (string), tier (string).

SignUpData: email, password, name, acceptMarketing? (boolean), inviteToken? (string — from invite link ?invite_token= query param).

SignUpResult: requiresVerification (boolean), email (string).

Same-origin API proxy — cookie mode setup

When using tokenStorage: 'cookie' (the default), auth tokens are stored as httpOnly cookies. Because api.githat.io is a different origin, cookies it sets are inaccessible to your app's domain. The SDK's githatApiProxy() helper solves this by mounting a Next.js route handler that proxies requests to api.githat.io and re-emits the Set-Cookie headers on your origin.

Mount the proxy at /api/githat/[...path]/route.ts using githatApiProxy() from @githat/nextjs/server, then configure GitHatProvider with apiUrl: '/api/githat'.

The proxy strips the following inbound headers to prevent client spoofing: Authorization, x-githat-user-id, x-githat-email, x-githat-user-email, x-githat-business-id, x-githat-org-id, x-githat-org-slug, x-githat-role, x-user-id, x-email, x-business-id, x-role, x-customer-id, x-customer-email, x-operator-user-id, x-operator-email, x-operator-business-id, x-operator-role, x-githat-mfa-trusted, plus hop-by-hop headers (host, connection, content-length, accept-encoding).

The proxy also strips the Domain= attribute from all Set-Cookie headers so cookies are scoped to the consumer's origin instead of api.githat.io.

Important caveat from ClickReserv's production setup: Node's fetch implementation silently drops the Cookie request header (forbidden per the WHATWG Fetch spec). If you use githatApiProxy() as-is, the upstream Lambda may not receive the auth cookie. ClickReserv works around this by implementing a custom proxy that reads the githat_access cookie and re-injects it as Authorization: Bearer. The SDK's built-in githatApiProxy() does handle cookie forwarding through the __Host-githat_mfa_trusted cookie (converted to x-githat-mfa-trusted header), but the Authorization fallback is not built in. Check your proxy's behavior in production.

// src/app/api/githat/[...path]/route.ts
import { githatApiProxy } from '@githat/nextjs/server';
export const { GET, POST, PUT, PATCH, DELETE, OPTIONS } = githatApiProxy();

OAuth callback routes

For each OAuth provider, you need a callback page that renders the corresponding Callback component. The default redirect URI pattern is /auth/{provider}/callback relative to window.location.origin (set per-call via oauthRedirectBase on SignInForm or redirectUri on individual buttons).

Apple is a special case: Apple uses response_mode=form_post, so Apple's callback must be a Route Handler (not a page) that accepts a POST, extracts code and user from the form body, and redirects to an intermediate page. The intermediate page then renders AppleCallback with the code and user from searchParams.

// app/auth/google/callback/page.tsx
import { GoogleCallback } from '@githat/nextjs';

export default function Page() {
  return (
    <GoogleCallback
      redirectUrl="/dashboard"
      newUserRedirectUrl="/onboarding"
    />
  );
}

Token storage and localStorage keys

In 'localStorage' mode, the SDK uses the following keys: githat_access_token (access JWT), githat_refresh_token (refresh JWT), githat_user (JSON-serialized GitHatUser), githat_org (JSON-serialized GitHatOrg).

In 'cookie' mode, the cookie names are githat_access (access token) and githat_refresh (refresh token). In production with __Host- prefix enforcement (used by ClickReserv), these become __Host-githat_access and __Host-githat_refresh.

Environment variables

The following env var names appear in the SDK source and in fleet app usage. None of these are read by the SDK itself — they are passed as config props. The names listed here are the conventions used by the fleet apps.

NEXT_PUBLIC_GITHAT_PUBLISHABLE_KEY or GITHAT_PUBLISHABLE_KEY (the fleet passes it at runtime from a non-public key to avoid baking it into the bundle): publishable key for GitHatProvider.

GITHAT_APP_ID / NEXT_PUBLIC_GITHAT_APP_ID: the app's GitHat-Apps record UUID, used as the audience for verifyToken.

GITHAT_API_URL (optional, default 'https://api.githat.io'): overrides the upstream URL in proxy.ts verifyToken calls.

NEXT_PUBLIC_TURNSTILE_SITE_KEY: Cloudflare Turnstile site key passed as captchaSiteKey in GitHatConfig.