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

graph LR DISCOVER["GET /.well-known/openid-configuration"] --> JWKS["GET /jwks"] DISCOVER --> AUTHZ["GET /authorize"] DISCOVER --> TOKEN["POST /token"] DISCOVER --> USERINFO["GET /userinfo"] DISCOVER --> REVOKE["POST /revoke"] AUTHZ -->|auth code| TOKEN TOKEN -->|access_token| USERINFO TOKEN -->|access_token| REVOKE

Discovery document

Standard OIDC discovery — third-party RPs autoconfigure from this URL.

Bash
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

GrantWhen to use
authorization_code + PKCEBrowser apps, mobile apps. Mandatory PKCE (S256).
refresh_tokenLong-lived sessions; only granted when client requests offline_access scope.
client_credentialsService accounts. No user context — token represents the client itself.
device_codeHeadless devices, CLI logins on machines without browsers.

Persistence tables

SQL
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

Bash
# 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

JSON
{
  "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

Questions?

Reach out for help with integration, deployment, or custom domain codecs.