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
scpclaim (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
- Delegation scope vocabulary
- Embed Companion widget — add AI chat after auth
- Passport identity deep dive — DIDs, key management, recovery