Hanzo IAM
Identity and Access Management for the Hanzo AI Platform
Hanzo IAM
API reference · Hanzo IAM API → — every endpoint, generated from the OpenAPI spec.
Hanzo IAM is the identity provider for the Hanzo platform, served at hanzo.id. It is a standards-compliant OpenID Connect (OIDC) and OAuth 2.0 authorization server that issues the JWTs every other Hanzo service trusts. It supports password, social OAuth2, Web3 wallet, WebAuthn/passkey, LDAP, SAML, device-code, and MFA authentication.
| Issuer | https://hanzo.id |
| API prefix | /v1/iam |
| Discovery | https://hanzo.id/.well-known/openid-configuration |
| JWKS | https://hanzo.id/v1/iam/.well-known/jwks |
| Source | github.com/hanzoai/iam |
Every Hanzo IAM endpoint lives under the single canonical prefix /v1/iam. The standard discovery path /.well-known/openid-configuration is served as a back-compatible alias so off-the-shelf OIDC clients work out of the box.
Features
- Per-host white-label: one deployment serves many domains, each with its own issuer, branding, and JWKS
- Web3 Authentication: MetaMask, WalletConnect, Coinbase, Rainbow
- WebAuthn / Passkeys: FIDO2 passwordless authentication with hardware keys or biometrics
- LDAP & SAML: Directory integration and SAML 2.0 SP/IdP
- MFA: TOTP, WebAuthn, SMS, and email verification
- Post-quantum tokens: JWT signing with
MLDSA65(ML-DSA-65, FIPS 204) in addition toRS256/ES256 - Multi-tenancy: Organizations, applications, and user isolation with RBAC
OIDC Endpoints
All endpoints are under https://hanzo.id/v1/iam.
| Endpoint | Path |
|---|---|
| Discovery | GET /.well-known/openid-configuration (alias) · GET /v1/iam/.well-known/openid-configuration |
| JWKS | GET /v1/iam/.well-known/jwks |
| Authorize | GET /v1/iam/oauth/authorize |
| Token | POST /v1/iam/oauth/token |
| Userinfo | GET /v1/iam/oauth/userinfo |
| Logout / end-session | GET,POST /v1/iam/oauth/logout |
| Introspect (RFC 7662) | POST /v1/iam/oauth/introspect |
| Revoke (RFC 7009) | POST /v1/iam/oauth/revoke |
| Device code | POST /v1/iam/oauth/device |
| Dynamic client registration | POST /v1/iam/oauth/register |
Applications: the <org>-<app> convention
Every consumer of Hanzo IAM registers an application, and the application's name follows one rule:
app slug = <org>-<app>For example: hanzo-brain, hanzo-cloud, hanzo-chat, acme-verify. The organization that owns the data goes in the organization field; the app name is namespaced with the org so it stays globally unique. Your application's client_id and the aud (audience) claim of issued tokens both default to this <org>-<app> slug.
Register an application
curl -X POST https://hanzo.id/v1/iam/add-application \
-H "Authorization: Bearer $IAM_ADMIN_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"owner": "admin",
"name": "hanzo-brain",
"displayName": "Hanzo Brain",
"organization": "hanzo",
"cert": "cert-built-in",
"tokenFormat": "JWT",
"grantTypes": ["authorization_code", "refresh_token"],
"redirectUris": ["https://brain.hanzo.ai/callback"]
}'clientId and clientSecret are generated automatically if omitted. Store the returned clientSecret in Hanzo KMS — never in source or plaintext config.
For idempotent, declarative provisioning (CI/CD, operators), use POST /v1/iam/admin/applications/upsert with an Authorization: Bootstrap <token> header instead of a session/admin JWT.
Authorization Code + PKCE Flow
This is the recommended flow for web apps, SPAs, and mobile clients.
Discover
curl https://hanzo.id/.well-known/openid-configurationThe issuer and every endpoint come back scoped to the requesting host (hanzo.id).
Redirect to authorize
https://hanzo.id/v1/iam/oauth/authorize
?response_type=code
&client_id=hanzo-brain
&redirect_uri=https://brain.hanzo.ai/callback
&scope=openid%20profile%20email
&state=RANDOM_STATE
&code_challenge=<S256(verifier)>
&code_challenge_method=S256The user authenticates on the branded login page and is redirected back with ?code=...&state=....
Exchange the code for tokens
curl -X POST https://hanzo.id/v1/iam/oauth/token \
-d grant_type=authorization_code \
-d code=$AUTH_CODE \
-d redirect_uri=https://brain.hanzo.ai/callback \
-d client_id=hanzo-brain \
-d client_secret=$CLIENT_SECRET \
-d code_verifier=$PKCE_VERIFIERResponse:
{
"access_token": "eyJhbGciOiJSUzI1NiIs...",
"id_token": "eyJhbGciOiJSUzI1NiIs...",
"refresh_token": "eyJhbGciOiJSUzI1NiIs...",
"token_type": "Bearer",
"expires_in": 3600
}Get user info
curl https://hanzo.id/v1/iam/oauth/userinfo \
-H "Authorization: Bearer $ACCESS_TOKEN"Validating tokens in a resource server
Any Hanzo service validates a hanzo.id token offline: fetch the JWKS once, then verify each request locally.
- Fetch the signing keys from
https://hanzo.id/v1/iam/.well-known/jwks(cache bykid). - Verify the JWT signature (
RS256by default). - Require
iss == https://hanzo.id. - Require
aud == <org>-<app>(your ownclient_id). - Read
sub(the user ID) andowner(the user's organization) to scope data.
import { createRemoteJWKSet, jwtVerify } from 'jose'
const JWKS = createRemoteJWKSet(new URL('https://hanzo.id/v1/iam/.well-known/jwks'))
export async function verify(token: string) {
const { payload } = await jwtVerify(token, JWKS, {
issuer: 'https://hanzo.id',
audience: 'hanzo-brain', // your <org>-<app> client_id
})
return { userId: payload.sub, org: payload.owner as string }
}The Hanzo Gateway performs this validation at the edge and injects trusted X-Org-Id, X-User-Id, and X-User-Email headers (stripping any client-supplied identity headers) for upstream services.
White-labeling by domain
Hanzo IAM is multi-tenant white-label by design. A single deployment serves many domains:
- Per-host issuer — the issuer, authorization endpoint, and JWKS are derived per request from
X-Forwarded-Host(set by Hanzo Ingress). Each domain returns its own discovery document. - Per-app branding — logo, title, favicon, theme color, and custom CSS are configured on each organization and application, and resolved on the login page by
client_id(or by org in the path,/login/{org}).
This is how hanzo.id presents Hanzo branding while the same engine can serve entirely separate, independently-branded identity surfaces for other tenants.
Authentication Methods
| Method | Notes |
|---|---|
| Password | argon2id hashing |
| OAuth2 social | GitHub, Google, Microsoft, Apple, Discord, X |
| Web3 wallets | MetaMask, WalletConnect, Coinbase, Rainbow |
| WebAuthn / Passkeys | FIDO2 hardware keys and biometrics |
| LDAP | Per-organization directory integration |
| SAML 2.0 | Service provider and identity provider |
| Device code | CLIs and input-constrained devices |
| MFA | TOTP, WebAuthn, SMS, email |
SDK
import { signIn } from '@hanzo/iam/browser'
await signIn({
iamUrl: 'https://hanzo.id',
clientId: 'hanzo-brain', // your <org>-<app> slug
redirectUri: 'https://brain.hanzo.ai/callback',
})Next Steps
The canonical IAM_* environment variable contract for consumer services
How IAM stores and reads its own secrets from Hanzo KMS
Multi-party computation authentication for secure key recovery
Secret management for client secrets and signing keys
How is this guide?
Last updated on