@bulwarkauth/react

React components and hooks for Bulwark authentication.

Installation

npm install @bulwarkauth/react
# or
pnpm add @bulwarkauth/react

BulwarkProvider

Wrap your application tree with BulwarkProvider. All hooks and components must be descendants of this provider.

import { BulwarkProvider } from "@bulwarkauth/react";

export default function App({ children }: { children: React.ReactNode }) {
  return (
    <BulwarkProvider publishableKey="pk_live_...">
      {children}
    </BulwarkProvider>
  );
}

Props

| Prop | Type | Required | Description | |------|------|----------|-------------| | publishableKey | string | Preferred | Your pk_live_ or pk_test_ key | | config | BulwarkConfig | Legacy | Object with tenantId and baseUrl. Use publishableKey instead |

The publishableKey prop resolves your app's configuration (tenant, branding, enabled auth methods) from the /api/v1/auth/identify endpoint on initialization. Token refresh is handled automatically.


Pre-built Components

<SignIn />

Renders a complete sign-in form with email/password, magic link, and social login options based on your app's configuration.

import { SignIn } from "@bulwarkauth/react";

export default function LoginPage() {
  return (
    <SignIn
      signUpUrl="/signup"
      forgotPasswordUrl="/forgot-password"
      onSignIn={(user) => console.log("Signed in:", user.id)}
    />
  );
}

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | appearance | BulwarkAppearance | — | Theme and style overrides | | onSignIn | (user: User) => void | — | Callback after successful sign-in | | signUpUrl | string | — | Link shown at the bottom of the form | | forgotPasswordUrl | string | — | Link for password reset flow | | hideFooter | boolean | false | Hides the "Secured by Bulwark" footer |


<SignUp />

Renders a complete registration form.

import { SignUp } from "@bulwarkauth/react";

export default function SignUpPage() {
  return (
    <SignUp
      signInUrl="/login"
      onSignUp={(user) => console.log("Registered:", user.id)}
    />
  );
}

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | appearance | BulwarkAppearance | — | Theme and style overrides | | onSignUp | (user: User) => void | — | Callback after successful registration | | signInUrl | string | — | Link shown at the bottom of the form | | hideFooter | boolean | false | Hides the "Secured by Bulwark" footer |


<UserButton />

Renders an avatar button with a dropdown menu for profile navigation and sign-out. Renders nothing when signed out.

import { UserButton } from "@bulwarkauth/react";

function Header() {
  return (
    <nav>
      <UserButton
        afterSignOutUrl="/"
        profileUrl="/settings/profile"
      />
    </nav>
  );
}

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | appearance | BulwarkAppearance | — | Theme and style overrides | | afterSignOutUrl | string | "/" | Redirect URL after sign-out | | onProfileClick | () => void | — | Override default profile navigation | | profileUrl | string | — | URL for the profile page link |


<UserProfile />

Renders a full profile management UI (email, display name, avatar, password change, MFA setup).

import { UserProfile } from "@bulwarkauth/react";

export default function ProfilePage() {
  return <UserProfile appearance={{ theme: "light" }} />;
}

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | appearance | BulwarkAppearance | — | Theme and style overrides |


Hooks

useBulwark()

Returns the full Bulwark context. Use this when you need low-level access to the client or need to trigger a user refresh.

import { useBulwark } from "@bulwarkauth/react";

function Example() {
  const { user, isAuthenticated, isLoading, refreshUser } = useBulwark();

  if (isLoading) return <Spinner />;
  if (!isAuthenticated) return null;

  return (
    <div>
      <p>{user.email}</p>
      <button onClick={refreshUser}>Refresh</button>
    </div>
  );
}

Return value

| Field | Type | Description | |-------|------|-------------| | client | BulwarkClient | The underlying API client instance | | user | User \| null | The currently authenticated user | | isAuthenticated | boolean | true when a valid session exists | | isLoading | boolean | true during initial session resolution | | appConfig | AppConfig \| null | Resolved app configuration from publishable key | | setUser | (user: User \| null) => void | Manually update the user in context | | refreshUser | () => Promise<void> | Re-fetch the current user from the API |


useAuth()

Returns auth action methods for building custom forms.

import { useAuth } from "@bulwarkauth/react";

function CustomLoginForm() {
  const { login, isLoading, error } = useAuth();

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault();
    const form = e.currentTarget;
    await login({
      email: form.email.value,
      password: form.password.value,
    });
  };

  return (
    <form onSubmit={handleSubmit}>
      <input name="email" type="email" />
      <input name="password" type="password" />
      {error && <p>{error.message}</p>}
      <button type="submit" disabled={isLoading}>Sign in</button>
    </form>
  );
}

Return value

| Field | Type | Description | |-------|------|-------------| | register | (opts) => Promise<User> | Create a new account | | login | (opts) => Promise<User> | Sign in with email + password | | requestMagicLink | (email: string) => Promise<void> | Send a magic link email | | verifyMagicLink | (token: string) => Promise<User> | Exchange magic link token for session | | sendEmailOtp | (email: string) => Promise<void> | Send a one-time code | | verifyEmailOtp | (email, code) => Promise<User> | Verify OTP and establish session | | logout | () => Promise<void> | Sign out and clear session | | isLoading | boolean | true during any in-flight request | | error | BulwarkError \| null | Last error, cleared on next action |


useMFA()

Handles TOTP and WebAuthn MFA enrollment and verification.

import { useMFA } from "@bulwarkauth/react";

function MfaSetup() {
  const { enrollTotp, verifyTotp, enrolledFactors } = useMFA();
}

usePasskey()

Handles passkey (WebAuthn) registration and authentication.

import { usePasskey } from "@bulwarkauth/react";

function PasskeyButton() {
  const { registerPasskey, authenticateWithPasskey } = usePasskey();
}

useAgent()

Manages agent credential sessions from the client side.

import { useAgent } from "@bulwarkauth/react";

function AgentPanel() {
  const { createSession, activeSession, revokeSession } = useAgent();
}

useAppConfig()

Returns the resolved application configuration for the current publishable key.

import { useAppConfig } from "@bulwarkauth/react";

function BrandedHeader() {
  const config = useAppConfig();
  return <img src={config?.branding.logoUrl} alt={config?.name} />;
}

ProtectedRoute

Wraps a route to require authentication. Renders fallback when the user is not signed in, or unauthorized when the user lacks a required role.

import { ProtectedRoute } from "@bulwarkauth/react";

function App() {
  return (
    <Routes>
      <Route
        path="/admin"
        element={
          <ProtectedRoute
            roles={["admin"]}
            fallback={<Navigate to="/login" />}
            unauthorized={<div>Access denied</div>}
          >
            <AdminDashboard />
          </ProtectedRoute>
        }
      />
    </Routes>
  );
}

Props

| Prop | Type | Default | Description | |------|------|---------|-------------| | roles | string[] | — | Required roles. All listed roles must be present | | fallback | ReactNode | null | Rendered when not authenticated | | unauthorized | ReactNode | null | Rendered when authenticated but missing a required role |


Appearance customization

Pass a BulwarkAppearance object to any component's appearance prop.

const appearance: BulwarkAppearance = {
  theme: "dark",
  variables: {
    colorPrimary: "#6366f1",
    colorBackground: "#0f172a",
    colorText: "#f1f5f9",
    fontFamily: "Inter, sans-serif",
    borderRadius: "8px",
  },
  elements: {
    card: { boxShadow: "0 4px 32px rgba(0,0,0,0.4)" },
    primaryButton: { fontWeight: "600" },
  },
};

<SignIn appearance={appearance} />

BulwarkAppearance type

| Field | Type | Description | |-------|------|-------------| | theme | "light" \| "dark" | Base color scheme | | variables | AppearanceVariables | CSS variable overrides | | elements | Record<string, CSSProperties> | Per-element style overrides |

AppearanceVariables

| Variable | Description | |----------|-------------| | colorPrimary | Primary action color (buttons, links) | | colorBackground | Card background color | | colorText | Primary text color | | colorDanger | Error/destructive state color | | fontFamily | Font stack | | borderRadius | Border radius applied to cards and inputs |


Token refresh

Access tokens are refreshed automatically before they expire. No manual intervention is required. The refresh runs in the background; components re-render only if the user object changes.