PasskeyBase

React SDK

The @passkeybase/react package provides hooks, components, and route guards for integrating passkey authentication into React applications.


Installation

Terminal
npm install @passkeybase/react @passkeybase/js

Provider Setup

Wrap your app with PasskeyBaseProviderat the root. This initializes the SDK and makes auth state available to all child components via React Context.

main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { PasskeyBaseProvider } from '@passkeybase/react';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <PasskeyBaseProvider
    clientId="pk_live_abc123"
    domain="myapp.auth.ing"
    redirectUri={window.location.origin + '/callback'}
  >
    <App />
  </PasskeyBaseProvider>
);

Auth Hooks

useAuth()

Access authentication state and methods from any component inside the provider.

Dashboard.tsx
import { useAuth } from '@passkeybase/react';

function Dashboard() {
  const {
    isAuthenticated,
    isLoading,
    loginWithRedirect,
    logout,
    getAccessToken,
  } = useAuth();

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (!isAuthenticated) {
    return <button onClick={() => loginWithRedirect()}>Log in</button>;
  }

  const handleApiCall = async () => {
    const token = await getAccessToken();
    const res = await fetch('/api/data', {
      headers: { Authorization: `Bearer ${token}` },
    });
    const data = await res.json();
    console.log(data);
  };

  return (
    <div>
      <h1>Welcome back</h1>
      <button onClick={handleApiCall}>Fetch data</button>
      <button onClick={() => logout()}>Log out</button>
    </div>
  );
}

useUser()

Access the current user's profile. Returns undefined when not authenticated.

Profile.tsx
import { useUser } from '@passkeybase/react';

function Profile() {
  const { user, isLoading } = useUser();

  if (isLoading) return <div>Loading profile...</div>;
  if (!user) return null;

  return (
    <div>
      <h2>{user.displayName}</h2>
      <p>{user.email}</p>
      <p>User ID: {user.sub}</p>
      <p>Passkeys registered: {user.credentials.length}</p>
    </div>
  );
}

Protected Routes

The AuthGuard component redirects unauthenticated users to the login page. It accepts an optional fallback prop shown while the auth check is in progress.

App.tsx
import { AuthGuard } from '@passkeybase/react';
import { BrowserRouter, Routes, Route } from 'react-router-dom';

function App() {
  return (
    <BrowserRouter>
      <Routes>
        <Route path="/" element={<HomePage />} />
        <Route path="/callback" element={<CallbackPage />} />
        <Route
          path="/dashboard"
          element={
            <AuthGuard fallback={<div>Redirecting to login...</div>}>
              <Dashboard />
            </AuthGuard>
          }
        />
        <Route
          path="/settings"
          element={
            <AuthGuard>
              <Settings />
            </AuthGuard>
          }
        />
      </Routes>
    </BrowserRouter>
  );
}

Login and Logout

Pre-built Buttons

Drop-in LoginButton andLogoutButton components. They accept all standard button props for styling.

Header.tsx
import { LoginButton, LogoutButton } from '@passkeybase/react';

function Header() {
  const { isAuthenticated } = useAuth();

  return (
    <nav>
      <h1>My App</h1>
      {isAuthenticated ? (
        <LogoutButton className="btn btn-secondary">
          Sign out
        </LogoutButton>
      ) : (
        <LoginButton className="btn btn-primary">
          Sign in with Passkey
        </LoginButton>
      )}
    </nav>
  );
}

Programmatic Login

Use the useAuth() hook for full control over the login and logout flow.

CustomLogin.tsx
import { useAuth } from '@passkeybase/react';

function CustomLogin() {
  const { loginWithRedirect, logout } = useAuth();

  const handleLogin = () => {
    loginWithRedirect({
      scopes: ['openid', 'profile', 'email'],
      state: 'return=/onboarding',
    });
  };

  const handleLogout = () => {
    logout({ returnTo: window.location.origin });
  };

  return (
    <div>
      <button onClick={handleLogin}>Log in</button>
      <button onClick={handleLogout}>Log out</button>
    </div>
  );
}

Handling the Callback

Create a callback page that calls handleRedirectCallback() and then navigates the user into the app.

CallbackPage.tsx
import { useAuth } from '@passkeybase/react';
import { useEffect } from 'react';
import { useNavigate } from 'react-router-dom';

function CallbackPage() {
  const { handleRedirectCallback, isLoading } = useAuth();
  const navigate = useNavigate();

  useEffect(() => {
    const handle = async () => {
      const result = await handleRedirectCallback();
      const returnTo = result.state || '/dashboard';
      navigate(returnTo);
    };
    handle();
  }, []);

  return <div>Completing login...</div>;
}

Passkey Enrollment

The useEnrollment() hook manages the passkey enrollment flow. It provides loading state and error handling out of the box.

PasskeySettings.tsx
import { useEnrollment, useAuth } from '@passkeybase/react';

function PasskeySettings() {
  const { getAccessToken } = useAuth();
  const { enrollPasskey, isEnrolling, error } = useEnrollment();

  const handleEnroll = async () => {
    // Fetch an enrollment token from your backend
    const token = await getAccessToken();
    const res = await fetch('/api/enrollment-token', {
      method: 'POST',
      headers: { Authorization: `Bearer ${token}` },
    });
    const { enrollmentToken } = await res.json();

    // Open the enrollment popup
    await enrollPasskey(enrollmentToken);
    alert('Passkey added successfully!');
  };

  return (
    <div>
      <h2>Passkey Management</h2>
      <button onClick={handleEnroll} disabled={isEnrolling}>
        {isEnrolling ? 'Enrolling...' : 'Add a new passkey'}
      </button>
      {error && <p className="error">{error.message}</p>}
    </div>
  );
}

TypeScript Support

All hooks, components, and props are fully typed. Types are re-exported from @passkeybase/js for convenience.

TypeScript types
import type { User, AuthResult } from '@passkeybase/js';
import { useAuth, useUser } from '@passkeybase/react';

// All hooks are fully typed
const { user } = useUser();         // user: User | undefined
const { isAuthenticated } = useAuth(); // isAuthenticated: boolean

// Provider props are typed via PasskeyBaseProviderProps
interface PasskeyBaseProviderProps {
  clientId: string;
  domain: string;
  redirectUri?: string;
  scopes?: string[];
  audience?: string;
  cacheLocation?: 'memory' | 'localstorage';
  children: React.ReactNode;
}

Next steps