Skip to main content
The trust attestor is also an OpenID Provider. A service can add a “Sign in with AFAuth” button so a human signs in and lands in the same account their agent already created — the agent-first analogue of “Sign in with Google”.
Defined in AFAP-0008 and recognized normatively at spec/core.md §10.8. It builds entirely on the (iss, sub_h) principal of §10.4 — no new trust root, no new key.

The one-line idea

An AFAuth account is already keyed on the pairwise principal (iss, sub_h) (§10.4.4 account grouping). That pair is an OIDC (issuer, subject). So the only piece missing for human sign-in was an issuer endpoint: something that authenticates the human and returns a signed token carrying that sub_h. The trust attestor — which already holds the human↔agent binding and already derives sub_h — now exposes exactly that. The payoff: your agent signs up (attested), creating an account keyed on (iss, sub_h). Later the human clicks Sign in with AFAuth, authenticates at trust.afauth.org, and the returned id_token carries the same sub_h — so they land in the account the agent built, not a new empty one.

Three relationships, one principal

The trust attestor sits across three flows. Linking and claiming already existed; sign-in is the new one — and all three resolve to the same (iss, sub_h):
FlowWho starts itWhat it does
LinkagentBinds the agent DID to a human at trust.afauth.org so an attested_only service accepts it. Transfers no ownership.
Claimagent (invites)Hands ownership of one account to a human — the ceremony.
Sign inhumanThe human authenticates and lands in the (iss, sub_h) account their agent created.
  Human                    Your service (RP)            trust.afauth.org (OP)
    │  click "Sign in        │                              │
    │   with AFAuth"         │  302 → /oidc/authorize       │
    │───────────────────────▶│  (PKCE, state, nonce)        │
    │                        │─────────────────────────────▶│
    │            authenticate as the human  ◀───────────────│
    │                        │   302 back with ?code         │
    │                        │◀─────────────────────────────│
    │                        │  POST /oidc/token (verifier)  │
    │                        │─────────────────────────────▶│
    │                        │   id_token { sub = sub_h }    │
    │                        │◀─────────────────────────────│
    │   session in the       │  resolve (iss, sub_h)         │
    │   agent's account  ◀───│  → the agent-created account  │

What sign-in is — and isn’t

  • Authentication, not ownership. Signing in proves the human is the principal behind the (iss, sub_h) account. It does not by itself run the claim ceremony or raise the §7.5 owner-binding floor. A service MAY treat a first sign-in as enough to show the human their agent’s account, and MAY separately run a claim to establish a recoverable owner (§10.8.5).
  • No PII crosses the wire. The id_token carries only the pairwise sub_h — never an email, name, or other identifier — the same constraint trust attestations honour. Two colluding services still cannot correlate the same human.
  • Audience-bound. The id_token’s aud is your service_did; a token minted for another service cannot be replayed at yours — the same redirection defence attestations get.

The convergence — and the one way to break it

The agent’s attestation carries iss: "afauth-trust" (a bare string). The sign-in id_token carries iss: "https://trust.afauth.org" (a URL — OIDC issuers must be URLs). They denote the same attestor. A service that keys accounts on (iss, sub_h) and offers sign-in MUST treat both forms as one issuer identity — canonicalize to the URL — before lookup. Skip it and the human lands in a brand-new empty account instead of their agent’s. It’s a silent correctness bug, which is why the spec makes it a normative requirement (§10.8.4). The fix is one line:
const canonicalIss = (iss) =>
  iss === "afauth-trust" ? "https://trust.afauth.org" : iss;

Precondition: the account needs a sub_h

Sign-in finds an account by (iss, sub_h), so the account must have a sub_h. That value is written when the agent signs up attested — i.e. after it has been linked to the human. An agent that signed up as an unattested guest first has no sub_h yet; it binds one on its next attested request, after which sign-in resolves. The guide covers this case.

Build it

Add Sign in with AFAuth

Service-side how-to: register the client, run the OIDC flow, resolve the account.

The trust attestor

The OpenID Provider behind sign-in — the same issuer that mints attestations.

Trust API reference

OIDC endpoints, the id_token shape, and client registration.

AFAP-0008

The proposal and normative core.md §10.8 text.