One signin. Many sites. No middleman.
AAM ID is the optional identity layer for AAM. A user signs in once with an identity provider of their choice, gets back a signed JWT credential, and uses it across every AAM-enabled site that trusts that provider. Sites verify signatures locally — the identity provider is OUT of the request path after credential issuance. This is what federation looks like.
Why this design
Per-site consent flows (the AAM 0.1 baseline) work but require users to re-verify their identity at every new site. After 5 sites, most users give up. AAM ID solves this by externalizing identity to a one-time flow at any conformant provider; sites trust whoever they configure.
This is NOT gatekeeping. We run a reference provider at whatcanido.dev as a demonstration and public utility. Anthropic, Google, OpenAI, the EU government, or your cousin Honza could each run their own conformant AAM ID provider tomorrow. Sites pick which providers they accept by listing them in their manifest.
1. Credential format (JWT, RS256)
AAM ID credentials are JSON Web Tokens signed with RS256. Header:
{
"alg": "RS256",
"typ": "JWT",
"kid": "<key id from issuer's JWKS>"
}Claims:
{
"iss": "https://whatcanido.dev", // issuer URL
"sub": "alice@example.com", // verified identity
"aud": "salon-aurora.vercel.app", // intended audience (or "any")
"iat": 1709913600,
"exp": 1712505600,
"agent_vendor": "claude-code", // which agent holds this
"scopes": ["book:appointment", "cancel:appointment"],
"email_verified": true,
"verification_method": "google_oidc", // how identity was proven
"name": "Alice Smith" // optional
}2. JWKS endpoint
Every AAM ID provider exposes its public verification keys at:
GET <issuer>/.well-known/aam-jwks.json
{
"keys": [
{
"kty": "RSA",
"alg": "RS256",
"use": "sig",
"kid": "key-2026-04",
"n": "<base64url modulus>",
"e": "AQAB"
}
]
}Sites cache this for 24 hours, refetch when a credential's kidisn't in cache. No request to the issuer is needed for normal credential validation — sites verify signatures locally with the cached public key.
3. Scope conventions (verb:resource)
Scopes use the format verb:resource. Common verbs:
read:catalog— read service catalog, pricesbook:appointment— create a bookingcancel:appointment— cancel an existing bookingpurchase:product— buy a productsubscribe:newsletter— sign up for emails
Sites accept the scopes they need; agents request the scopes their use case requires. Mismatched scopes return HTTP 403 with scope_required:<name>.
4. Connect flow (out-of-band, chat-agent friendly)
- Agent attempts an authenticated action without a credential → site responds 401 with
accepted_identity_proofsarray including AAM ID provider URLs. - Agent constructs a connect URL:
<provider>/id/connect?agent={vendor}&scopes={a,b}&site={site}. - Agent shows URL to user. User opens, authenticates with the provider's method (Google OIDC in our reference impl), sees what permissions are requested, approves.
- Provider mints a signed JWT, displays it on a page with a copy button. User pastes it back to the agent.
- Agent retries the original action with
Authorization: Bearer <jwt>. - Site verifies signature against issuer's JWKS, accepts the claim.
5. Site manifest declaration
Sites declare what identity proofs they accept in their /.well-known/agent-actions.json:
{
"auth": {
"type": "delegated_oauth",
"required_for": ["book_appointment"],
"accepted_identity_proofs": [
{
"type": "aam_id",
"issuer": "https://whatcanido.dev",
"jwks_url": "https://whatcanido.dev/.well-known/aam-jwks.json",
"connect_url_pattern": "https://whatcanido.dev/id/connect?agent={agent}&scopes={scopes}&site={site}"
},
{
"type": "aam_id",
"issuer": "https://identity.anthropic.com",
"jwks_url": "https://identity.anthropic.com/.well-known/aam-jwks.json"
},
{
"type": "per_site_consent",
"authorize_url": "https://this-site.com/api/aam/authorize",
"token_url": "https://this-site.com/api/aam/token"
}
]
}
}6. Cross-site reputation API (optional, network effect)
Conformant providers MAY expose two endpoints for cross-site reputation tracking:
POST <issuer>/api/aam-id/track
{
"credential": "<JWT the site received>",
"site": "<reporting site domain>",
"scope": "book:appointment",
"status": "ok" | "failed"
}
GET <issuer>/api/aam-id/reputation/<sha256(email)>
→ { issued_count, successful_actions, site_count, trust_band, ... }Sites query reputation to apply trust bands: new identities get tighter limits, established identities get smooth flow. Privacy: identity is hashed by site before lookup, no PII flows to provider during reputation queries.
7. Becoming a conformant provider
To be a conformant AAM ID provider, you MUST:
- Generate an RSA-2048 (or stronger) keypair, expose JWKS at
/.well-known/aam-jwks.json - Issue JWTs with the claim shape in §1, signed RS256
- Include
issequal to your origin URL - Verify identity before issuing (Google OIDC, Apple Sign In, BankID, etc.)
- Set reasonable
exp(we recommend 30 days)
You SHOULD:
- Implement the connect URL pattern in §4 for chat-agent compatibility
- Expose reputation API per §6 for network-effect benefit
- Document supported scopes
- Support credential revocation via a status endpoint
8. Why we're not the gatekeeper
We run the reference provider, but the protocol is open. Sites freely choose which providers they trust. If we go down, sites switch issuer in their manifest — minutes of work. If we misbehave, sites stop trusting ouriss. This is RSS for identity: distributed, substitutable, no single chokepoint.
Our moat is being first, being the reference impl, having the most cross-site reputation data. Not being the only option. If we are the only option, we have failed.
Reference implementation
/.well-known/aam-jwks.json— our public keys/id/connect— connect flow entry point/api/aam-id/track— reputation tracker (POST only)/api/aam-id/reputation/<hash>— reputation query