> ## Documentation Index
> Fetch the complete documentation index at: https://docs.hexclave.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Authentication

> Production-ready sign-in, a real user directory, and session verification - without building auth yourself

export const UserDirectorySkeleton = () => {
  const Frame = ({label, children}) => <div className="overflow-hidden rounded-2xl border border-zinc-950/10 bg-white dark:border-white/10 dark:bg-zinc-900">
      <div className="flex items-center gap-2 border-b border-zinc-950/10 bg-zinc-950/[0.03] px-3 py-2 dark:border-white/10 dark:bg-white/[0.03]">
        <div className="flex gap-1.5">
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
        </div>
        <span className="ml-1 text-[11px] font-medium text-zinc-400 dark:text-zinc-500">{label}</span>
      </div>
      <div className="p-4">{children}</div>
    </div>;
  const cell = w => <div className="h-2 rounded-full bg-zinc-200/90 dark:bg-zinc-700/80" style={{
    width: w
  }} />;
  const badge = (text, tone) => {
    const tones = {
      green: "bg-emerald-100 text-emerald-700 dark:bg-emerald-900/40 dark:text-emerald-400",
      zinc: "bg-zinc-200 text-zinc-600 dark:bg-zinc-700 dark:text-zinc-300"
    };
    return <span className={"inline-block rounded-full px-2 py-0.5 text-[10px] font-medium " + tones[tone]}>
        {text}
      </span>;
  };
  const row = (initial, color, w1, w2, status) => <div className="grid grid-cols-[1.4fr_1.6fr_0.8fr] items-center gap-3 border-b border-zinc-950/[0.06] px-3 py-2.5 last:border-b-0 dark:border-white/[0.06]">
      <div className="flex items-center gap-2.5">
        <div className="flex h-6 w-6 shrink-0 items-center justify-center rounded-full text-[10px] font-bold text-white" style={{
    backgroundColor: color
  }}>
          {initial}
        </div>
        {cell(w1)}
      </div>
      {cell(w2)}
      <div>{badge(status[0], status[1])}</div>
    </div>;
  return <div className="not-prose my-6">
      <Frame label="Users">
        <div className="overflow-hidden rounded-xl border border-zinc-950/[0.06] dark:border-white/[0.06]">
          <div className="grid grid-cols-[1.4fr_1.6fr_0.8fr] gap-3 border-b border-zinc-950/[0.06] bg-zinc-950/[0.02] px-3 py-2 text-[10px] font-semibold uppercase tracking-wide text-zinc-400 dark:border-white/[0.06] dark:bg-white/[0.02] dark:text-zinc-500">
            <span>User</span>
            <span>Email</span>
            <span>Status</span>
          </div>
          {row("A", "#6b5df7", "68px", "120px", ["Active", "green"])}
          {row("M", "#0ea5e9", "84px", "104px", ["Active", "green"])}
          {row("S", "#f59e0b", "56px", "132px", ["Invited", "zinc"])}
          {row("R", "#ef4444", "72px", "92px", ["Active", "green"])}
        </div>
      </Frame>
    </div>;
};

export const AuthMethodsSkeleton = () => {
  const ACCENT = "#6b5df7";
  const Frame = ({label, children}) => <div className="overflow-hidden rounded-2xl border border-zinc-950/10 bg-white dark:border-white/10 dark:bg-zinc-900">
      <div className="flex items-center gap-2 border-b border-zinc-950/10 bg-zinc-950/[0.03] px-3 py-2 dark:border-white/10 dark:bg-white/[0.03]">
        <div className="flex gap-1.5">
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
        </div>
        <span className="ml-1 text-[11px] font-medium text-zinc-400 dark:text-zinc-500">{label}</span>
      </div>
      <div className="p-4">{children}</div>
    </div>;
  const toggle = on => <div className={"flex h-4 w-7 items-center rounded-full px-0.5 " + (on ? "justify-end" : "justify-start bg-zinc-200 dark:bg-zinc-700")} style={on ? {
    backgroundColor: ACCENT
  } : undefined}>
      <div className="h-3 w-3 rounded-full bg-white" />
    </div>;
  const row = (label, on) => <div className="flex items-center justify-between border-b border-zinc-950/[0.06] py-2.5 last:border-b-0 dark:border-white/[0.06]">
      <div className="flex items-center gap-2.5">
        <div className="h-5 w-5 rounded-md bg-zinc-200 dark:bg-zinc-700" />
        <span className="text-[12px] font-medium text-zinc-600 dark:text-zinc-300">{label}</span>
      </div>
      {toggle(on)}
    </div>;
  return <div className="not-prose my-6">
      <Frame label="Auth methods">
        <div className="flex flex-col">
          {row("Email & password", true)}
          {row("Magic link / OTP", true)}
          {row("Passkey", true)}
          {row("Google", true)}
          {row("GitHub", true)}
          {row("Microsoft", false)}
        </div>
      </Frame>
    </div>;
};

export const SignInSkeleton = () => {
  const ACCENT = "#6b5df7";
  const Frame = ({label, children}) => <div className="overflow-hidden rounded-2xl border border-zinc-950/10 bg-white dark:border-white/10 dark:bg-zinc-900">
      <div className="flex items-center gap-2 border-b border-zinc-950/10 bg-zinc-950/[0.03] px-3 py-2 dark:border-white/10 dark:bg-white/[0.03]">
        <div className="flex gap-1.5">
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
          <div className="h-2.5 w-2.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
        </div>
        <span className="ml-1 text-[11px] font-medium text-zinc-400 dark:text-zinc-500">{label}</span>
      </div>
      <div className="p-4">{children}</div>
    </div>;
  const provider = label => <div className="flex items-center justify-center gap-2 rounded-lg border border-zinc-200 bg-white px-3 py-2 dark:border-zinc-700 dark:bg-zinc-900">
      <div className="h-3.5 w-3.5 rounded-full bg-zinc-300 dark:bg-zinc-600" />
      <span className="text-[12px] font-medium text-zinc-500 dark:text-zinc-400">{label}</span>
    </div>;
  const field = label => <div className="flex flex-col gap-1.5">
      <span className="text-[11px] font-medium text-zinc-400 dark:text-zinc-500">{label}</span>
      <div className="h-8 rounded-lg border border-zinc-200 bg-zinc-50 dark:border-zinc-700 dark:bg-zinc-800/60" />
    </div>;
  return <div className="not-prose my-6">
      <Frame label="/handler/sign-in">
        <div className="mx-auto flex max-w-xs flex-col gap-3">
          <div className="mb-1 flex flex-col items-center gap-2">
            <div className="h-9 w-9 rounded-xl" style={{
    backgroundColor: ACCENT
  }} />
            <div className="text-[13px] font-semibold text-zinc-700 dark:text-zinc-200">Sign in</div>
          </div>
          <div className="grid grid-cols-2 gap-2">
            {provider("Google")}
            {provider("GitHub")}
          </div>
          <div className="flex items-center gap-3 py-0.5">
            <div className="h-px flex-1 bg-zinc-200 dark:bg-zinc-700" />
            <span className="text-[10px] uppercase tracking-wide text-zinc-400 dark:text-zinc-500">or</span>
            <div className="h-px flex-1 bg-zinc-200 dark:bg-zinc-700" />
          </div>
          {field("Email")}
          {field("Password")}
          <div className="mt-1 flex h-8 items-center justify-center rounded-lg text-[12px] font-semibold text-white" style={{
    backgroundColor: ACCENT
  }}>
            Continue
          </div>
        </div>
      </Frame>
    </div>;
};

<Note>
  **For agents/LLMs:** This is a high-level *marketing* overview of the Authentication app, not an implementation reference. To actually build auth, use [Setup](/guides/getting-started/setup) and the [Authentication guide](./guide) (sign-in methods, auth pages, session reads, and route protection), then the deeper pages: [Hosted vs. Handler](/guides/going-further/hosted-vs-handler), [Auth providers](./auth-providers), [JWTs & session verification](./jwts), [Sign-up rules](./sign-up-rules), and [Connected accounts](./connected-accounts).
</Note>

Authentication is the foundation every other Hexclave app builds on. You get hosted sign-in pages, every common login method, and a complete user directory - wired up in minutes, not weeks. Below are the questions developers actually ask, and the honest answers.

## Can I add sign-in without building forms?

Yes. Prefer **hosted components** (`urls: { default: { type: "hosted" } }` in [Setup](/guides/getting-started/setup)) — Hexclave hosts the sign-in UI and keeps it updated. Or mount `<HexclaveHandler />` on your own `/handler/[...]` route if you want auth pages on your domain. See [Hosted vs. Handler](/guides/going-further/hosted-vs-handler).

```tsx title="app/handler/[...hexclave]/page.tsx" theme={null}
import { HexclaveHandler } from "@hexclave/next"; // replace `next` with your framework SDK

export default function Handler() {
  return <HexclaveHandler fullPage />;
}
```

<SignInSkeleton />

You never write a form, manage a redirect, or hand-roll a reset flow. Want it inside your own layout? Drop in the prebuilt components - `<SignIn />`, `<SignUp />`, `<AccountSettings />` - and tune them with props. Want a fully custom UI? Build your own against the SDK's auth methods.

## Can I offer every login method?

Yes - and you flip each one on or off from the dashboard, no redeploy required.

* **Email & password** with secure reset
* **Magic links / OTP** for passwordless sign-in
* **Passkeys** (WebAuthn) for phishing-resistant login
* **12 OAuth providers** - Google, GitHub, Microsoft, Apple, Discord, Facebook, LinkedIn, Twitch, Spotify, GitLab, Bitbucket, and X - plus your own OpenID Connect provider
* **Two-factor authentication** (TOTP)

<AuthMethodsSkeleton />

Shared Hexclave keys work out of the box for Google, GitHub, Microsoft, and Spotify — best for a [development environment](/guides/going-further/local-vs-cloud-dashboard). Swap in your own client ID and secret for production. See [Auth Providers → Shared vs. Custom OAuth Keys](./auth-providers#shared-vs-custom-oauth-keys).

## Can I get the current user anywhere in my app?

Yes. The same user object is available on the client (as a hook) and the server (as an async call), with full TypeScript types.

```tsx theme={null}
// Client component - re-renders when the user changes
const user = useUser();

// Server component / route handler / action
const user = await hexclaveServerApp.getUser();
```

Need to gate a page? Pass `{ or: "redirect" }` and unauthenticated visitors are sent to sign-in automatically:

```tsx theme={null}
const user = useUser({ or: "redirect" });
```

## Can I manage my users?

Yes. Every sign-up creates a real profile - connected accounts, auth methods, metadata, and activity - not just a token. Search, filter, edit, and export from the dashboard, or do the same from code with the server SDK.

<UserDirectorySkeleton />

Store your own data on a user with `clientMetadata`, `clientReadOnlyMetadata`, and `serverMetadata`, so you rarely need a separate users table of your own.

## Can I verify sessions on my backend?

Yes. Hexclave issues standard JWTs you can verify locally against a JWKS endpoint - no round-trip to Hexclave on every request - so auth checks stay fast even in middleware and edge functions. See [JWTs & session verification](./jwts).

## Can I control who gets in?

Yes. Write [sign-up rules](./sign-up-rules) to allow, reject, or restrict accounts by email domain, country, auth method, or built-in [fraud-protection](./fraud-protection) risk scores. Suspicious accounts can be held in a [restricted](./restricted-users) state for review instead of blocked outright.

## Can I own my data and self-host?

Yes. Hexclave is open source (MIT client, AGPLv3 server). Run it fully self-hosted, export your users from the dashboard whenever you want, and avoid lock-in. The same SDK and APIs work on the managed service and self-hosted. A [development environment](/guides/going-further/local-vs-cloud-dashboard) is for building against Hexclave locally — some production-only setup (custom OAuth keys, custom email, payments) lives in the [cloud dashboard](https://app.hexclave.com).

## Start here

1. [Set up Hexclave](/guides/getting-started/setup) in your project (a few minutes) — prefer hosted components; see [Hosted vs. Handler](/guides/going-further/hosted-vs-handler).
2. Turn on the auth methods you want (and mount `<HexclaveHandler />` only if you chose the own-handler path).
3. Use `useUser()` / `getUser()` to read the session and protect routes.

Everything else - [teams](../teams/overview), [payments](../payments/overview), [emails](../emails/overview), [analytics](../analytics/overview) - keys off this same user directory.

Ready for a start-to-finish walkthrough — sign-in methods, auth pages, reading the user, and protecting routes? Read the [Authentication guide](./guide).
