← Back

Patch notes — Document Portal: customer login provisioning + SSO handoff (2026-06-15)

Ethica.no 2d18e58 · EthicaDocServer 9948766. The folder-per-company integration already created each company’s docs folder and the portal rendered it as an access-gated section — but no human was ever granted access. The “provision a portal login” half was only a settings panel and a Test connection button. This wires up the missing half, for both password and SSO customers. Full design: ../../ethica-no/components/docs-integration.md. Ethica.no Docker smoke 157/0.


1 — Password customers: auto-provisioned login ✅

New ethica/core/portal.py — a stdlib token-auth client for the portal machine API (POST /api/admin/{provision,sections,deprovision}, Bearer READPAGE_ADMIN_TOKEN). Best-effort: every call returns a bool and never raises, so portal trouble never blocks user management. Mirrors the Secbitz platform’s lib/portal.ts.

Hooked into the customer-user lifecycle (admin Users page + the company-admin Add team member on /account):

EventCallEffect
Customer createdprovisionportal login (same password) granted [company name]
Password changedprovisionpassword re-synced (idempotent upsert)
Disabled / role off customersections []grants revoked
Re-enabledsections [section]company section re-granted
Deleteddeprovisionlogin removed

The grant section is the exact company name — the same string as the docs folder, so it lines up with the portal’s slug-matched access check.

2 — SSO customers: a signed handoff ✅

An SSO (PocketID) customer has only a random throwaway password in Ethica.no, and the portal has no SSO of its own — so the password mirror can’t admit them. Added a signed single-sign-on bridge instead:

  • ethica.noportal_sso_url() mints a short-lived (~2 min) HMAC-SHA256 token over {u, exp, s|a}, signed with the shared READPAGE_ADMIN_TOKEN (no new secret). Sections are taken from the user’s company server-side, never from the browser. /account shows “Open documentation portal ↗”GET /account/docs-portal redirects to dokument.ethica.no/sso?token=….
  • portal (EthicaDocServer) — GET /ssoverify_sso_token() (constant-time signature
    • expiry check) → upserts the login (no password needed), grants the section, opens a session, lands them in. Admin/staff carry a:1 → every section.

One button serves password and SSO users. The mint↔verify pair was round-trip verified byte-for-byte (tampered signature, wrong key, and expired tokens all rejected).

3 — Configuration

None added. Both halves reuse the existing Document Portal URL + admin token under Settings → Docs. Security: server-derived sections, ~2-minute token expiry, constant-time verify, TLS-only session cookies.

Verification

py_compile clean across both repos; token mint/verify round-trip green; Ethica.no Docker smoke 157/0 (no regression in settings / account / dispatch).