OIDC Provider (Tetrapus as the OP)
Flip the federation: Tetrapus can be the IdP. Third-party applications register as OIDC clients and add a "Sign in with Tetrapus" button. Useful for partner integrations, embedded analytics portals, and any in-house tool that should inherit Tetrapus users, groups, and ABAC attributes without re-implementing auth.
Use cases
- Partner portals. A vendor builds a dashboard that reads from Tetrapus APIs and wants single sign-on tied to the same identity store.
- Embedded apps. An internal tool (Grafana, custom React app, Jupyter Hub) federates against Tetrapus instead of standing up its own user db.
- Service-to-service. A CI runner needs short-lived OAuth tokens with Tetrapus-issued claims for downstream API calls.
Endpoint surface
Discovery document
Standard OIDC discovery — third-party RPs autoconfigure from this URL.
curl https://tetrapus.example.com/api/v1/oidc/.well-known/openid-configuration
{
"issuer": "https://tetrapus.example.com/api/v1/oidc",
"authorization_endpoint": "https://tetrapus.example.com/api/v1/oidc/authorize",
"token_endpoint": "https://tetrapus.example.com/api/v1/oidc/token",
"userinfo_endpoint": "https://tetrapus.example.com/api/v1/oidc/userinfo",
"jwks_uri": "https://tetrapus.example.com/api/v1/oidc/jwks",
"revocation_endpoint": "https://tetrapus.example.com/api/v1/oidc/revoke",
"response_types_supported": ["code"],
"grant_types_supported": ["authorization_code","refresh_token","client_credentials","urn:ietf:params:oauth:grant-type:device_code"],
"code_challenge_methods_supported": ["S256"],
"id_token_signing_alg_values_supported": ["RS256","ES256"],
"scopes_supported": ["openid","profile","email","groups","offline_access"],
"subject_types_supported": ["public"]
} Supported grant types
| Grant | When to use |
|---|---|
| authorization_code + PKCE | Browser apps, mobile apps. Mandatory PKCE (S256). |
| refresh_token | Long-lived sessions; only granted when client requests offline_access scope. |
| client_credentials | Service accounts. No user context — token represents the client itself. |
| device_code | Headless devices, CLI logins on machines without browsers. |
Persistence tables
CREATE TABLE oidc_op_clients (
client_id TEXT PRIMARY KEY,
org_id TEXT NOT NULL REFERENCES orgs(id),
client_secret_hash TEXT NOT NULL, -- argon2id, NULL for public clients
client_type TEXT NOT NULL, -- 'public' | 'confidential'
redirect_uris TEXT NOT NULL, -- JSON array
grant_types TEXT NOT NULL, -- JSON array
scopes TEXT NOT NULL, -- JSON array
name TEXT NOT NULL,
created_at TEXT NOT NULL
);
CREATE TABLE oidc_op_authorization_codes (
code TEXT PRIMARY KEY, -- one-time-use
client_id TEXT NOT NULL,
user_id TEXT NOT NULL,
redirect_uri TEXT NOT NULL,
scopes TEXT NOT NULL,
code_challenge TEXT NOT NULL, -- PKCE
nonce TEXT,
expires_at TEXT NOT NULL -- 60s default
);
CREATE TABLE oidc_op_access_tokens (
token_id TEXT PRIMARY KEY, -- jti claim
client_id TEXT NOT NULL,
user_id TEXT, -- NULL for client_credentials
scopes TEXT NOT NULL,
expires_at TEXT NOT NULL,
revoked_at TEXT
); Token exchange example
# After the user has been redirected back to the RP with ?code=...
curl -X POST https://tetrapus.example.com/api/v1/oidc/token \
-u "$CLIENT_ID:$CLIENT_SECRET" \
-d "grant_type=authorization_code" \
-d "code=$AUTH_CODE" \
-d "redirect_uri=https://rp.example.com/callback" \
-d "code_verifier=$PKCE_VERIFIER"
# {
# "access_token": "eyJhbGc...",
# "id_token": "eyJhbGc...",
# "refresh_token": "rt_01HVXY...",
# "token_type": "Bearer",
# "expires_in": 3600,
# "scope": "openid profile email groups"
# } id_token claim shape
{
"iss": "https://tetrapus.example.com/api/v1/oidc",
"sub": "01HV4ABC...", // Tetrapus user_id
"aud": "client_01HVXY...",
"exp": 1714099200,
"iat": 1714095600,
"nonce": "n-0S6_WzA2Mj",
"email": "user@acme.example",
"email_verified": true,
"name": "Ada Lovelace",
"given_name": "Ada",
"family_name": "Lovelace",
"groups": ["engineering","oncall"],
"org_id": "01HVQRG..." // Tetrapus-specific extension
} Status
The OIDC provider library is complete: discovery, JWKS rotation, code/token issuance, PKCE verification, all four grants, userinfo, and revocation are implemented and unit-tested. The REST handlers are currently stubbed — they return wired-up shapes but defer to placeholder logic for authorize, token, and userinfo. Wiring this into the handlers is a small mechanical task scheduled before GA. Until then, the OP role is library-callable but not network-exposed.
Related
- OIDC Consumer — the other direction
- SAML 2.0
- ← Federation overview
Questions?
Reach out for help with integration, deployment, or custom domain codecs.