OIDC Consumer (Relying Party)
Tetrapus acts as an OpenID Connect Relying Party. Users sign in with Google, Okta, Azure AD or any OIDC-compliant IdP, and Tetrapus mints a local session bound to the verified email. The Authorization Code flow with PKCE is the only flow exposed — implicit and hybrid are deliberately omitted.
Per-Org IdP configuration
Each Org defines its own set of OIDC providers in
oidc_identity_providers. Provider records
carry the discovery URL, client ID, client secret (encrypted at rest), allowed callback path, and a
short alias used in URL paths.
CREATE TABLE oidc_identity_providers (
id TEXT PRIMARY KEY,
org_id TEXT NOT NULL REFERENCES orgs(id),
alias TEXT NOT NULL, -- "google", "acme-okta"
issuer_url TEXT NOT NULL, -- e.g. https://accounts.google.com
client_id TEXT NOT NULL,
client_secret TEXT NOT NULL, -- encrypted via SecretStore
scopes TEXT NOT NULL, -- "openid email profile"
enabled INTEGER NOT NULL DEFAULT 1,
UNIQUE (org_id, alias)
);
CREATE TABLE oidc_links (
user_id TEXT NOT NULL REFERENCES users(id),
provider_id TEXT NOT NULL REFERENCES oidc_identity_providers(id),
subject TEXT NOT NULL, -- "sub" claim from id_token
PRIMARY KEY (provider_id, subject)
); Authorization Code + PKCE flow
Link-by-verified-email semantics
First-time login from an IdP produces an id_token
with a stable sub claim and an
email_verified=true claim. Tetrapus uses the
verified email as a one-time bridge: if a local user with that email exists, an
oidc_links row is inserted
binding the IdP sub to that user. All subsequent logins go straight through the
(provider_id, subject) primary key — the email is no longer consulted.
If email_verified is missing or false, the
bind is refused. This prevents an attacker who controls a side IdP from claiming an unverified email and
hijacking a Tetrapus account.
REST routes
Start authorization
# Browser hits this directly. 302 to the IdP.
curl -i 'https://tetrapus.example.com/api/v1/auth/oidc/google/start?relay=/dashboard'
# HTTP/1.1 302 Found
# Set-Cookie: oidc_state=...; Path=/; HttpOnly; Secure; SameSite=Lax
# Location: https://accounts.google.com/o/oauth2/v2/auth?client_id=...&code_challenge=...&state=... Callback
# IdP redirects user-agent here. Should not be invoked manually.
curl -i 'https://tetrapus.example.com/api/v1/auth/oidc/google/callback?code=...&state=...'
# HTTP/1.1 302 Found
# Set-Cookie: dm_session=...; HttpOnly; Secure; SameSite=Strict
# Location: /dashboard Tokens stored, tokens discarded
Tetrapus does not persist the IdP's
access_token or
refresh_token. It only uses the
id_token at login time to verify identity,
then discards the entire OAuth grant. The Tetrapus session cookie is independently issued and rotated
on its own clock.
Discovery & JWKS caching
On first use, Tetrapus fetches {issuer_url}/.well-known/openid-configuration
and the JWKS URL it advertises. Both are cached in-memory for 1 hour. Key rotation by the IdP is
handled transparently — a key ID miss triggers a re-fetch.
Status
Shipped: Authorization Code + PKCE, JWKS verification, link-by-verified-email, per-Org provider config.
Deferred: dynamic client registration, RP-initiated logout via end_session_endpoint,
JIT user creation (must pre-provision).
Related
- OIDC Provider — the inverse: Tetrapus as the OP
- SAML 2.0 — alternative federation protocol
- SCIM 2.0 — auto-provision so the link-by-email step always finds a target
- ← Federation overview
Questions?
Reach out for help with integration, deployment, or custom domain codecs.