Getting Started

Go from zero to a working passkey-authenticated React app in under ten minutes.

In this tutorial we will build a minimal React application that authenticates a user with a passkey, reads their decentralized identity, and prints it to the console. By the end you will have a running Hyperauth integration and a clear mental model of the three pieces that make it work: the provider, the client, and the useHyperAuth hook.


What we will build

A single-page React app that:

  1. Wraps the app in HyperAuthProvider so every component can reach the client.
  2. Reads the client status with useHyperAuth.
  3. Creates a passkey and generates an identity with client.generate().
  4. Logs the resulting DID and Ethereum address to the browser console.

Prerequisites

  • Node 18+ and a package manager (npm, pnpm, or bun).
  • A React project bootstrapped with Vite or Create React App.
  • A browser that supports WebAuthn (Chrome, Firefox, or Safari).

Install the packages

Open a terminal at your project root and run:

npm install @hyperauth/sdk @hyperauth/react

@hyperauth/sdk is the core client. @hyperauth/react provides the React bindings.

Wrap your app in HyperAuthProvider

Open your root file — typically src/main.tsx or src/index.tsx — and wrap your <App /> in HyperAuthProvider:

// src/main.tsx
import React from 'react';
import ReactDOM from 'react-dom/client';
import { HyperAuthProvider } from '@hyperauth/react';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <HyperAuthProvider>
      <App />
    </HyperAuthProvider>
  </React.StrictMode>,
);

HyperAuthProvider accepts an optional config prop. We are omitting it here so it uses the defaults, which is correct for local development. The provider initialises the vault worker in the background; your app continues rendering immediately.

Read the client with useHyperAuth

Create src/App.tsx with the following content:

// src/App.tsx
import { useHyperAuth } from '@hyperauth/react';

export default function App() {
  const { client, status, isReady } = useHyperAuth();

  if (status === 'initializing') {
    return <p>Loading Hyperauth...</p>;
  }

  if (status === 'error') {
    return <p>Hyperauth failed to initialise. Check the console.</p>;
  }

  return (
    <div>
      <p>Hyperauth is ready.</p>
      <CreatePasskeyButton client={client!} />
    </div>
  );
}

useHyperAuth returns three values you will use constantly:

ValueTypeMeaning
clientHyperAuthClient | nullThe initialised client, or null while loading
status'initializing' | 'ready' | 'error'Current provider state
isReadybooleantrue when status === 'ready' and client !== null

Create a passkey and generate an identity

Add the CreatePasskeyButton component to the same file, below App:

// src/App.tsx (continued)
import { useState } from 'react';
import { createPasskey } from '@hyperauth/sdk';
import type { HyperAuthClient } from '@hyperauth/sdk';

function CreatePasskeyButton({ client }: { client: HyperAuthClient }) {
  const [busy, setBusy] = useState(false);

  async function handleCreate() {
    setBusy(true);
    try {
      // Ask the browser to create a passkey bound to the name "demo-user"
      const { credential } = await createPasskey('demo-user');

      // Generate a DID and encrypted key shares from the passkey credential
      const result = await client.generate(credential, {
        identifier: 'demo-user',
      });

      console.log('DID:', result.did);
      console.log('Ethereum address:', result.address);
      console.log('Full result:', result);
    } finally {
      setBusy(false);
    }
  }

  return (
    <button onClick={handleCreate} disabled={busy}>
      {busy ? 'Creating...' : 'Create passkey'}
    </button>
  );
}

client.generate() returns a GenerateResult:

interface GenerateResult {
  success: boolean;
  did: string;          // e.g. "did:key:z6Mk..."
  address: string;      // Ethereum address derived from the public key
  shares: EncryptedShares & { enclave_id: string };
  parsed_credential: Record<string, unknown>;
}

Keep result.shares — it is the encrypted key material you will pass to client.sign() in later operations.

Run the app and see your identity

Start the dev server:

npm run dev

Open http://localhost:5173, click Create passkey, and follow the browser's WebAuthn prompt. You'll see two lines appear in the browser console:

DID: did:key:z6Mk...
Ethereum address: 0x...

Notice that no server round-trip is needed to generate the identity. The DID and address are derived entirely inside the vault worker running in your browser.


What you have built

You now have a working Hyperauth integration with passkey authentication. The HyperAuthProvider manages the vault worker lifecycle, useHyperAuth gives any component access to the client, and client.generate() ties a passkey credential to a self-sovereign identity.

The shares value returned by generate() is the key you will carry into signing and registration. The next tutorial, Build a Registration Flow, shows how to use it to publish your identity on-chain.

On this page