bridge

An SSO federation gateway — the RP side of OIDC implemented from scratch against live, occasionally-broken upstreams.

GoOIDC RPcircuit breakerEd25519 live demo

What it is

An identity broker: applications integrate against one relying party — bridge — and bridge federates to the upstream providers (Google, Microsoft Entra ID, anything OIDC). Apps receive a short-lived signed assertion with normalized claims at their exact registered callback. The RP side of OIDC is implemented from scratch on purpose; JWT/JWKS verification reuses keysmith’s jose package — no third-party OIDC client library.

The architectural challenge

Upstream IdPs are dependencies bridge does not operate. When Microsoft is down, the failure mode to refuse is the slow one: every login holding a connection against a dead endpoint. Each provider gets a consecutive-failure circuit breaker (closed → open → half-open) around discovery, token exchange, and JWKS fetches; when open, /login/{provider} fails fast with a page listing healthy alternates. Cached JWKS are served up to 24 hours stale during an outage — a public key doesn’t go bad because the server hosting it is down — then verification fails closed, because unbounded staleness would make key revocation advisory. See ADR 0002.

Key design decisions

The Entra sharp edge

Entra’s multi-tenant discovery declares its issuer as the literal template https://login.microsoftonline.com/{tenantid}/v2.0. Compare naively and you reject every valid token; skip the check and you accept tokens from any tenant on Earth. bridge substitutes the token’s tid into the template — so iss and tid must agree — requires tid to exist, and optionally pins an allowed-tenant list. Covered by TestEntraTenantedIssuer (agree/disagree/missing/allowlist cases).

Security highlights

From the threat model: ID tokens are never accepted from the front channel — only from the code exchange bridge itself performs; the key set, never the token header, decides the verification algorithm; assertions go only to the exact registered app callback (no user-supplied redirect exists to abuse) and carry aud + a 2-minute TTL — a message, not a session; replayed callbacks are rejected uniformly.

Verified by

Code worth reading