Files
TSYS Group COO bde7845490 skills: agent-provisioning - codified identity stand-up workflow
Policy rulings (#942): unique passwords, Cloudron-only SSO accounts,
no reachableceo credentials, TOTP seeds MUST be captured to the vault.
Cloudron admin API wrapper, cookie-jar OIDC login, RFC-6238-tested
TOTP helper, per-system SSO/API references, incident log.
2026-09-08 04:07:14 -05:00

3.1 KiB

Cloudron admin API + OIDC (verified 2026-09-08)

Instance

  • Dashboard: https://my.knownelement.com (admin surface; this host also fronts the org's public vhosts — it is NOT the tailscale-only control box).
  • API token: vault item creds/cloudron, password field, 64 chars, service user api-token. Verified: reads full user directory.

Routes (this is a newer Cloudron — older route knowledge may not apply)

  • Users are addressed by uid-<uuid>, never username. GET /api/v1/users?limit=100 → list; GET /api/v1/users/<uid> → object (twoFactorAuthenticationEnabled is null/absent on objects even when enforced — the OIDC interaction is the source of truth).
  • There is NO POST /api/v1/login. Dashboard auth is its own OIDC browser flow; the SPA bundle (as of 2026-09-08) contains no login route.
  • There is NO admin route to reset another user's 2FA (PUT/PATCH/DELETE on /users/<uid> 2FA variants all 404; the dashboard UI only offers profile self-service enrollment). Consequence: TOTP seeds not captured at enrollment are unrecoverable without the founder.
  • Other useful routes seen in the SPA bundle: /api/v1/tokens, /api/v1/oidc/clients, /api/v1/groups, /api/v1/eventlog, /api/v1/app_passwords. Verify behavior at runtime before relying on a specific payload shape.

OIDC interaction login (how identity SSO works)

The GLPI/Gitea/Discourse/Redmine SSO flows all redirect here:

  1. GET <app SSO start point> → 302 to https://my.knownelement.com/openid/interaction/<uid> (Cloudron runs a node-oidc-provider at /openid). Cookies matter — keep a cookie jar.
  2. POST /openid/interaction/<uid>/login with JSON {"username": "<login>", "password": "<unique password>"}.
    • Field name is usernamelogin returns "A username must be non-empty string" (400).
    • Success → {"redirectTo": "..."} (or {"twoFactorRequired":true,"totpRequired":true} if 2FA is enabled).
    • Wrong credentials → 401 {"status":"Unauthorized","message":"Username and password does not match"}.
  3. GET the redirectTo (same cookie jar) → completes at the app.

Email-style usernames work (tsgstaff-coo@turnsys.com); bare usernames also exist (coo). Identity emails follow tsgstaff-<login>@turnsys.com.

2FA

  • TOTP enforced per user at the interaction step (twoFactorRequired/totpRequired in the login response).
  • Enrollment is profile self-service ONLY. Capture the base32 seed into the vault (sm setfield "<login> Cloudron" totp_seed <seed>) at enrollment — there is no admin reset and no recovery path.
  • Policy RULED (founder, 2026-09-08): TOTP seeds MUST be stored in Bitwarden. Enroll 2FA only when the seed is being captured to the vault item in the same step. Legacy identities whose seeds were never captured (the whole August batch) are reset-on-sight candidates: founder clears 2FA or hands over the seed, then re-enroll with capture.

Records: Redmine #942 — https://projects.knownelement.com/issues/942 · Discourse docs home: https://community.turnsys.com (skill topic pending admin-key provisioning, tracked on #942)