AbsoluteJS

Client SDK & Hooks

Primitives, not components. A framework-agnostic client (createAuthClient) over every endpoint plus thin React hooks (./react) — your forms, your styling, your decisions. HTMX is the special case (declarative server fragments); everything else gets the client + a hook / composable.

#createAuthClient

Every method returns { data, error } so you branch without try/catch.
Routes are configurable — override the ones you mounted on a custom path.
fetch is injectable for tests.
Same-origin cookies are sent by default.
TS
import { createAuthClient } from '@absolutejs/auth/client';

// Framework-agnostic SDK over every endpoint your auth() mounted. Every method
// returns { data, error } so you can branch without try/catch. Override routes
// if you mounted any of them at a custom path; pass a custom fetch for tests.
const client = createAuthClient({
  baseUrl: '',                  // default: relative paths
  credentials: 'same-origin',   // default; sends the session cookie
  routes: { login: '/api/v2/login' } // optional overrides
});

const { data, error } = await client.signIn.email({ email, password });
if (error) showError(error.message);
else if (data.status === 'mfa_required') openMfaPrompt();
else if (data.passwordCompromised) showResetBanner();
else go('/profile');

await client.passwordless.requestMagicLink({ email });
await client.mfa.challenge({ code });
const sessions = await client.sessions.list();

#React hooks

The @absolutejs/auth/react sub-export ships thin hooks over the client: { isPending, data, error, mutate, reset } (mutations) or { isPending, data, error, refetch, revoke } (useSessions). React is an optional peer dependency — install it if you use the hooks. Vue composables, Solid signals, and Svelte stores will land as sibling sub-exports wrapping the same client.

TSX
import { createAuthClient } from '@absolutejs/auth/client';
import {
  useMagicLink,
  useMfaChallenge,
  useSessions,
  useSignIn
} from '@absolutejs/auth/react';

// React peer-dep is optional. The hooks are thin: { isPending, data, error,
// mutate, reset } over the client (or { isPending, data, error, refetch,
// revoke } for the sessions query). Bring your own form / styling.
const client = createAuthClient();

function SignInForm() {
  const { mutate, isPending, error, data } = useSignIn(client);
  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      const form = new FormData(e.currentTarget);
      mutate({
        email: form.get('email') as string,
        password: form.get('password') as string
      });
    }}>
      <input name="email" type="email" />
      <input name="password" type="password" />
      <button disabled={isPending}>Sign in</button>
      {error && <p>{error.message}</p>}
      {data?.status === 'mfa_required' && <MfaPrompt />}
    </form>
  );
}

// Vue / Solid / Svelte composables wrap the same client — same shapes,
// different reactivity. 0.33.0 shipped the sibling sub-exports:
// @absolutejs/auth/vue, @absolutejs/auth/solid, @absolutejs/auth/svelte.

#Passkey autofill

0.37.0 ships usePasskeyAutofill across React, Vue, Solid, and Svelte.

1
YouMount usePasskeyAutofill
Mount the hook on the sign-in page.
2
BrowserPasskeys appear in autofill
The browser surfaces the user's saved passkeys in its autofill dropdown.
3
UserOne-tap sign-in
One tap signs them in.

@simplewebauthn/browser is an optional peer dep loaded via dynamic import; non-passkey consumers pay nothing.

TSX
import { createAuthClient } from '@absolutejs/auth/client';
import { usePasskeyAutofill } from '@absolutejs/auth/react';

// 0.37.0: WebAuthn conditional-UI. Mount on the sign-in page with an
// <input autocomplete="username webauthn"> and call start() in an effect;
// the browser surfaces the user's saved passkeys directly in its autofill
// dropdown — one tap and you're authenticated. @simplewebauthn/browser is
// an optional peer dep loaded dynamically; non-passkey consumers pay nothing.

const client = createAuthClient();

function SignInForm() {
  const { start, data, error, isPending } = usePasskeyAutofill(client);
  useEffect(() => { void start(); }, [start]);

  return (
    <form>
      <input
        name="email"
        autoComplete="username webauthn"
      />
      <input name="password" type="password" autoComplete="current-password" />
      <button disabled={isPending}>Sign in</button>
      {data?.status === 'authenticated' && <Redirect to="/dashboard" />}
      {error && <p>{error.message}</p>}
    </form>
  );
}

#Upgrade to passkey

useUpgradeToPasskey queries whether the signed-in user has any passkeys yet and exposes a shouldPrompt flag — true iff they have none. Wire it to a post-sign-in CTA so password users see a "save a passkey to this device?" prompt.

TSX
import { useUpgradeToPasskey } from '@absolutejs/auth/react';

// 0.37.0: "save a passkey to this device?" prompt for password users.
// shouldPrompt is true iff the signed-in user has zero passkeys; register()
// runs the WebAuthn registration ceremony and refetches the list.
function PostSignInPasskeyPrompt({ client }) {
  const { shouldPrompt, register, isPending } = useUpgradeToPasskey(client);
  if (!shouldPrompt) return null;

  return (
    <Banner>
      Save a passkey to this device for faster sign-in next time?
      <button onClick={() => register()} disabled={isPending}>
        Save passkey
      </button>
    </Banner>
  );
}