← Back

Patch notes — per-company API + SSO tabs (2026-06-15, commit `5107579`)

Adds two functional tabs next to Access on the company profile (/admin/company?id=X): an API tab (a per-company, scoped API token) and an SSO tab (per-company OpenID Connect group mapping). On main at commit 5107579, Docker smoke 116 / 0.


1 — API tab: a per-company, scoped API token ✅

Each company can now hold its own API token — high-entropy, separate from the global platform token in Settings → API. Generate / Reset / Revoke from the tab; it’s shown in a copyable field with a ready-to-paste curl example.

The token unlocks one new, read-only, scoped endpoint:

GET /api/company
  auth:  X-Company-Token: <token>     (or ?company_token=<token>)
  ->  { "company":  { id, name, org_no, website, active },
        "systems":  { online, degraded, offline, total, uptime_pct },
        "open_tickets": <n> }
  • The token resolves to exactly one company, so a holder can only ever read that company’s own profile, system health and open-ticket count — never another tenant’s data.
  • Lookup is constant-time (hmac.compare_digest over the small set of companies that have a token); an empty/blank token never matches.
  • The global X-API-Token gate (api_full) is untouched — this is an additive, parallel path for company-scoped machine access.

2 — SSO tab: explicit per-company group mapping ✅

An admin can map an OpenID Connect group straight to membership of this company. Set a Customer group and/or a Company-admin group; a federated user carrying that group in their groups claim is provisioned as a customer (or company-admin) of this company on sign-in.

  • Complements the existing global prefixes (customer: / companyadmin: in Settings → SSO), which still resolve by company id or name — the tab lists the group names those prefixes already map here, for reference.
  • _sso_resolve now checks the explicit per-company groups first at each privilege tier (company-admin before plain customer), then falls back to the prefixes, so the established precedence is preserved: admin > staff > company-admin > customer > reader.
  • A banner on the tab flags when SSO is disabled (the mappings simply take effect once it’s enabled in Settings → SSO).

3 — Under the hood

  • Schema: three idempotent columns on crm_companiesapi_token, sso_group, sso_admin_group (ALTER … ADD COLUMN IF NOT EXISTS; no semicolons in the SCHEMA comments, per the init-db split rule).
  • Both mutations (/admin/company/api, /admin/company/sso) go through the existing single CSRF check in the admin POST gate and are audited (company.api.reset / company.api.revoke / company.sso.save).
  • Access to the tabs themselves is gated by the existing can_access_company check on the company page.

Known limitation (unchanged by this patch)

can_access_company still treats all staff as having access to every company (the “Staff with access” assignments on the Access tab remain informational — see the spec-sheet’s open offer). The new API token is correctly scoped to one company; tightening staff scoping is a separate, future change.

Verification

py_compile clean · Docker smoke 116 / 0 — including the new tabs render, token generate / persist (48-hex) / revoke, /api/company returning scoped data, 401 without a token and 401 after revoke, and the SSO mapping saving + showing on the tab.