Architecture

Nothing exotic. Everything verifiable.

The complete technical specification of the Legal.id identity system: protocols, endpoints, token formats, claim sets, assurance semantics, and data handling. Written to be evaluated, not believed.

1 · System

One issuer, many relying parties.

Legal.id is a standalone OpenID Connect identity provider operated by Lextronica, Inc. Relying parties (first: Jane) integrate over standard OIDC — no proprietary SDK, no custom protocol. Applications never see credentials; they receive signed tokens carrying only consented claims.

EndpointPathNotes
Discovery/.well-known/openid-configurationCanonical metadata; everything below is advertised here
Authorization/oauth/authorizeBrowser redirect; consent screen for third-party clients
Token/oauth/tokenCode exchange + refresh; client secret over TLS
UserInfo/oauth/userinfoBearer access token; same claim set as the ID token
JWKS/oauth/discovery/keysRSA public key(s), kid-tagged
Revocation/oauth/revokeRFC 7009; client-authenticated
End session/logout/oidcRP-initiated logout; validated post_logout_redirect_uri
DID document/u/{handle}/did.jsondid:web resolution target
Attestation/u/{handle}/attestation.jwtFresh EdDSA-signed JWS of the identity's public facts
Verify (human)/verify?u={handle}Public lookup page; performs and displays the JWS check

2 · Protocol

OAuth 2.0 + OIDC, held to OAuth 2.1 rules.

Grant typesauthorization_code + refresh only. No implicit, no password grant, no client credentials.
PKCERequired (S256). Enforced server-side for public clients; confidential clients send it too (Jane does).
ClientsConfidential only in v1; registered by the operator (no self-serve registration). Exact redirect-URI matching; HTTPS required outside development.
State / nonceRound-tripped and validated by the reference RP integration; nonce is embedded in the ID token.
ConsentExplicit consent screen, per scope, for third-party clients. First-party clients are pre-authorized. Consent pages send frame-ancestors 'none' (clickjacking defense, app-wide).
Authorization codeSingle use · expires in 60 seconds
Access tokenOpaque (not a JWT) · 2 hours · revocable
Refresh tokenRotated on every use; the replaced token is invalidated
ID tokenJWT, RS256, kid in header · 2 hours · aud = client id, iss = https://legal.id
Signing keyRSA-2048. Published at JWKS. Rotation is a deploy procedure: new key issues immediately; old tokens age out within the 2-hour TTL. Multi-key JWKS publication is a scheduled follow-up.
SubjectOpaque stable UUID (sub), never the handle — handles can evolve without breaking RP links

3 · Claims

Scopes map to claims. Nothing extra.

ScopeClaims released (ID token + UserInfo)
openidsub (opaque UUID), standard JWT claims (iss, aud, exp, iat, nonce, auth_time)
emailemail, email_verified (boolean — RPs are instructed not to link accounts when false)
profilename, preferred_username (the handle), legal.id/tier, legal.id/did

4 · Assurance

The tier states what we verified. Nothing more.

Tiers are defined by the verification event that produced them and are carried in the token (legal.id/tier), so relying parties make their own trust decisions.

TierVerification performedRecorded
DigitalControl of the email address proven via confirmation linkemail_verified_at
CoreIdentity corroborated by sign-in with a verified Google accountgoogle_verified_at + provider uid
VerifiedForm I-9 employment-eligibility verification completed by Legal.io — an in-person review of government identity documentsAttestation event only: basis, attesting admin, timestamp. Documents, document numbers and SSNs are never stored or entered.

I-9 attestation semantics

Only Legal.id administrators can attest, and only for people whose I-9 Legal.io has actually completed. The attestation sets a re-attestation date 36 months out (reattest_on, printed on the card) and writes an immutable audit event. Attestations are revocable at any time; revocation drops the account to the tier its remaining verifications support.

Unverified accounts

An account whose email is not yet confirmed is Unverified: its card renders, but tokens carry email_verified: false and relying parties are told not to link accounts on it.

5 · Decentralized identity

did:web, resolvable by anything.

Resolution

did:web:legal.id:u:{handle}  ⇩ resolves tohttps://legal.id/u/{handle}/did.json  ⇩ publishesEd25519 key (publicKeyMultibase, z6Mk…)profile + verification service endpoints

Method: did:web (path form). Upgrade path to did:webvh (verifiable history) as the method matures. Key custody is server-side today, with private keys encrypted at rest; holder-controlled keys arrive with the wallet seam.

Attestation (JWS)

GET /u/{handle}/attestation.jwt  ⇩ compact JWS, alg=EdDSAheader: { alg, typ, kid: {did}#key-1 }payload: { iss, sub: {did}, handle,  legal.id/tier, legal.id/issued,  iat, exp: iat+600 }  ⇩ verify againstdid.json → Ed25519 public key

Ten-minute validity: it is a fresh, timestamped assertion of current status, not a bearer credential. Wallet-held verifiable credentials (OpenID4VCI/VP, selective disclosure) are the scheduled next step and are not claimed until they ship.

6 · Sessions, sign-in, logout

Session semantics.

Sign-in methodsPasskeys (WebAuthn, discoverable credentials, counter-checked) · password (min 10 chars, bcrypt) with mandatory email verification · optional Google corroboration
Session storeServer-side records (IP + user agent), signed httponly cookie, SameSite=Lax, Secure in production
Lifetime30 days absolute · 14 days idle · users see all active sessions and can revoke any, or all others at once
Fixation defenseRails session reset on every privilege transition
RP-initiated logoutend_session_endpoint in discovery. Ends the IdP session; redirects only to origins registered by a known client (open-redirect defense); state round-tripped.
App revocationUsers revoke any connected app from the dashboard — all its tokens and grants are invalidated immediately. Clients can revoke via RFC 7009.
RecoveryEmail-based password reset (30-minute single-use token); completing a reset invalidates all other sessions. Generic responses on request (no account enumeration).
AuditAppend-only event log: sign-ins (and failures), sign-outs, token issue/refresh/revocation, consents, passkey add/remove/failed attempts, verification changes, attestations. Recent events shown to the user; write failures alarm rather than drop silently.

7 · Abuse resistance

Rate limits.

SurfaceLimit
Password login10/min per IP · 6 per 5 min per email
Signup8 per 5 min per IP (invite brute-force defense; invite redemption is atomic and capped)
Token endpoint30/min per IP
Password reset5 per 15 min per IP
Passkey endpoints30/min per IP

TLS is forced (HSTS). All responses send frame-ancestors 'none'.

8 · Data

What we hold, what RPs get.

Stored: email, handle, display name, credential material (password hash / passkey public keys / Google uid), an Ed25519 DID keypair (private key encrypted at rest), and security logs. Never stored: plaintext passwords, I-9 documents or numbers, SSNs. Relying parties receive only consented claims. Retention and rights: see the privacy policy and terms.

9 · Roadmap

Where this goes.

Live now

The identity core.

OpenID Connect provider, passkey + password sign-in, invite-gated handles, the Legal.id card, DID documents with signed attestations, public verification, session and app management, I-9 attestation, audit logging — with Jane live as the first relying party.

Next

Credentials that travel.

Wallet-held verifiable credentials over OpenID4VCI/VP — starting with a Legal.io-attested professional credential using selective disclosure. The QR on every card becomes a live presentation endpoint.

Then

Organizations and delegation.

Firm accounts, delegated authority (firm → lawyer), and Legal.io production sign-in. Enterprise federation where the market demands it.

Context

Standards weather report.

W3C Verifiable Credentials 2.0 is a Recommendation; OpenID4VP/VCI 1.0 are final; EU eIDAS 2.0 wallets are mandated by end-2026. Passkeys passed five billion in active use. Legal.id is built to meet all of it.

Identity is infrastructure.

When trust matters, bring the ID built for it.

Get your Legal.id