Skip to main content

Login with HUMΛN Passport

Login with HUMΛN Passport

HUMΛN Passport is a portable, device-rooted identity. When a user logs into your app with Passport, they prove ownership of their private key via a WebAuthn biometric gesture. You get back a delegation token — a scoped JWT you can use to authorize calls to any HUMΛN service.

The full auth chain:

Device biometric → WebAuthn assertion → Session token → Delegation token (hdgt_)
     identity            authentication       session           authorization

Pick your layer

Layer What it looks like When to use
1 — React hook usePassportAuth() · <HumanPassportAuth /> React app — magic mode, zero config
2 — Web Component <human-passport-auth> Any framework — Vue, Angular, Svelte, vanilla HTML
3 — PassportAuth class new PassportAuth(config) Custom UI, complex workflows, server-side testing

Layer 1: React hook (Magic Mode)

npm install @human/passport @human/passport/react
// components/HumanAuth.tsx
'use client';
import { usePassportAuth } from '@human/passport/react';

export function HumanAuth({ onAuthenticated }: { onAuthenticated?: (token: string, did: string) => void }) {
  const { login, register, isAuthenticated, isLoading, did, delegationToken, error } =
    usePassportAuth({
      apiUrl: process.env.NEXT_PUBLIC_HUMAN_API_URL ?? 'https://api.haio.run',
    });

  if (isAuthenticated && delegationToken && did) {
    onAuthenticated?.(delegationToken, did);
    return <p>Authenticated as {did}</p>;
  }

  return (
    <div>
      {error && <p>{error.message}</p>}
      <button onClick={() => login()} disabled={isLoading}>
        Sign in with HUMΛN Passport
      </button>
      <button onClick={() => register()} disabled={isLoading}>
        Create Passport
      </button>
    </div>
  );
}

Or use the pre-built component for zero JSX:

import { HumanPassportAuth } from '@human/passport/react';

<HumanPassportAuth
  apiUrl={process.env.NEXT_PUBLIC_HUMAN_API_URL!}
  onAuthenticated={({ delegationToken, did }) => {
    // Send delegationToken to your server for verification
  }}
/>

Environment

# .env.local
NEXT_PUBLIC_HUMAN_API_URL=https://api.haio.run
HUMAN_API_URL=https://api.haio.run     # server-side verify

Layer 2: Web Component (any framework)

Works in vanilla HTML, Vue, Angular, Svelte — anywhere custom elements are supported.

npm install @human/passport
# OR use CDN (no install):
# <script type="module" src="https://cdn.haio.run/passport/latest/passport.js"></script>
<!-- Import registers <human-passport-*> custom elements automatically -->
<script type="module">
  import '@human/passport';
</script>

<human-passport-auth
  api-url="https://api.haio.run"
  default-capabilities="companion:chat"
  theme="auto"
></human-passport-auth>

<script>
  document.querySelector('human-passport-auth')
    .addEventListener('authenticated', (e) => {
      const { delegationToken, did } = e.detail;
      // delegationToken is ready to use — send to your server
    });
</script>

Available Web Components:

  • <human-passport-auth> — combined login + register
  • <human-passport-login> — existing passport only
  • <human-passport-register> — new passport creation
  • <human-passport-recover> — account recovery
  • <human-passport-recovery-setup> — recovery key enrollment

React wrapper:

'use client';
import { useEffect, useRef } from 'react';

export function HumanPassportWC({ onAuthenticated }: { onAuthenticated?: (token: string, did: string) => void }) {
  const ref = useRef<HTMLElement>(null);
  useEffect(() => {
    import('@human/passport').then(() => {
      const el = ref.current;
      if (!el) return;
      const handler = (e: Event) => {
        const { delegationToken, did } = (e as CustomEvent).detail;
        onAuthenticated?.(delegationToken, did);
      };
      el.addEventListener('authenticated', handler);
      return () => el.removeEventListener('authenticated', handler);
    });
  }, [onAuthenticated]);
  return <human-passport-auth ref={ref} api-url="https://api.haio.run" theme="auto" />;
}

Layer 3: PassportAuth class (Control Mode)

Full lifecycle control — use when building custom designs or server-side integrations.

npm install @human/passport
import { PassportAuth } from '@human/passport/browser';

const auth = new PassportAuth({
  humanApiUrl: 'https://api.haio.run',
  fetch: customFetch, // optional — inject for testing
});

// Convenience: authenticate = login + exchange in one call
const result = await auth.authenticate();
// result: { token: "hdgt_...", did: "did:human:...", capabilities: [...], expiresAt: "..." }

// Step-by-step control:
const session = await auth.login();
// → LoginResult { sessionToken, passportId, did, displayName, verificationTier, expiresAt }

const delegation = await auth.exchangeForDelegation(session.sessionToken, ['companion:chat']);
// → DelegationResult { token: "hdgt_...", delegationId, did, capabilities, expiresAt }

// New passport registration:
const minted = await auth.mint('My Name');
// → MintResult { did, passportId, displayName, publicKey, ... }

The delegation token

After auth, you hold token: "hdgt_...". This is a JWT that:

  • Starts with hdgt_ (HUMΛN Delegation Token)
  • Contains scp claim (array of scopes)
  • Is verified server-side via Authorization: Bearer hdgt_...

Always verify server-side:

// app/api/auth/verify/route.ts (Next.js App Router)
export async function POST(request: Request) {
  const { delegationToken } = await request.json();

  const res = await fetch(`${process.env.HUMAN_API_URL}/v1/sessions/verify`, {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      Authorization: `Bearer ${delegationToken}`,
    },
    body: JSON.stringify({ token: delegationToken }),
  });

  if (!res.ok) return Response.json({ error: 'Invalid token' }, { status: 401 });
  const verified = await res.json();
  // verified: { did: "did:human:...", scopes: ["companion:chat"], exp: 1734307200 }
  return Response.json({ verified: true, did: verified.did });
}

Common scopes

Scope What it grants
passport:login Create session + delegation token
companion:chat AI chat via Companion
kb:read:public Public KB access in Companion
kb:read:internal Internal KB (admin-granted)
human_api:agents:invoke Call agents directly
org:settings:read Read org settings

Testing

MockPassport server (no device required in CI):

human login --local   # starts MockPassport on :3777

Injectable fetch (unit tests, no server):

import { PassportAuth } from '@human/passport/browser';

const auth = new PassportAuth({
  humanApiUrl: 'http://localhost:3777',
  fetch: async (url, options) => {
    if (String(url).includes('/v1/passport/delegation')) {
      return new Response(JSON.stringify({
        token: 'hdgt_test_token',
        delegationId: 'dlg_test',
        did: 'did:human:test_user',
        capabilities: ['companion:chat'],
        passportId: 'psp_test',
        verificationTier: 1,
        identityTier: 1,
        createdAt: new Date().toISOString(),
        expiresAt: new Date(Date.now() + 3600000).toISOString(),
      }));
    }
    return fetch(url, options);
  },
});

const result = await auth.authenticate();
// result.token === 'hdgt_test_token'

Quick start with MCP scaffold

If you have the HUMΛN MCP server connected:

human.scaffold({ type: "passport-auth", framework: "nextjs-app-router" })

Returns ready-to-write files (components/HumanAuth.tsx + app/api/auth/verify/route.ts) plus install_cmd, env_vars, and next_steps.


Next steps

← All patterns