feat: SSO from GTI Gerenciador and the Gerenciador visual identity
Collaborators logged into GTI Gerenciador open GTI Fiscal without a second password. The panel signs a 60 s JWT (Ed25519; the Hub holds only the public key); the Hub checks signature, iss/aud/exp/nbf, maximum lifetime, nonce against a one-time state cookie set when the flow starts at GET /login (login-CSRF protection) and a single-use jti (sso_used_assertions). Collaborators are provisioned in admin_users with auth_source=GTI_GERENCIADOR and no password; a CHECK constraint makes a passwordless local account or a federated account with a password impossible (migration 0021). Sessions last FEDERATED_SESSION_SECONDS (1 h) and are renewed silently through the panel. The local admin stays as break-glass at /login/local, audited. Logout ends only the Hub session. Every SSO success/rejection (stable reason code), logout and local login is audited with the real client IP, never the token. verify_password now fails closed on a missing hash instead of raising. Visual identity: gti-theme.css is a verbatim copy of the panel's design system (tokens, sidebar, light/dark theme) plus its tema.js, sidebar.js and logo; control-panel.css keeps only Hub components, all colours from the panel tokens. Page structure and features unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -155,6 +155,44 @@ generate absolute links, but keep it accurate for anyone reading the config).
|
||||
`gti-fiscal-hub-port.service` (instructions in the unit file) so only the
|
||||
NPM reaches the port, re-applied on every Docker start.
|
||||
|
||||
## Human access: SSO from GTI Gerenciador
|
||||
|
||||
Collaborators never type a second password. GTI Gerenciador (the panel) is the
|
||||
identity authority; the Hub keeps its own sessions.
|
||||
|
||||
1. "Fiscal" in the panel sidebar -> panel `/sso/fiscal` -> Hub `GET /login`,
|
||||
which sets a one-time state cookie (`gti_sso_state`, HttpOnly, Secure,
|
||||
SameSite=None, 5 min) and redirects to `SSO_START_URL?state=...&next=...`.
|
||||
2. The panel requires its own login (normal login page if needed), checks the
|
||||
employee is active and auto-POSTs a JWT to `/auth/sso/callback`:
|
||||
Ed25519 (`EdDSA` only), `iss=gti-gerenciador`, `aud=gti-fiscal`,
|
||||
`sub=funcionario:<ID>`, 60 s, unique `jti`, `nonce`=state. Never in a URL.
|
||||
3. The Hub verifies signature/iss/aud/exp/nbf/lifetime, the nonce against the
|
||||
state cookie (login-CSRF protection), records the `jti`
|
||||
(`sso_used_assertions`, replay = rejected), provisions or refreshes the
|
||||
collaborator in `admin_users` (`auth_source=GTI_GERENCIADOR`, no password,
|
||||
enforced by `ck_admin_users_auth_source`) and opens a session of
|
||||
`FEDERATED_SESSION_SECONDS` (default 1 h; afterwards the same flow signs the
|
||||
user in again silently, which also picks up a deactivation in the panel).
|
||||
|
||||
Keys: a dedicated Ed25519 pair, used for nothing else (never reuse
|
||||
SESSION_SECRET, admin, Agent or DB secrets). The **private** key
|
||||
(raw 32-byte seed, base64url) goes only to the panel's `.env`
|
||||
(`SSO_FISCAL_CHAVE_PRIVADA`); the **public** key only to this Hub's `.env`
|
||||
(`SSO_GERENCIADOR_CHAVE_PUBLICA`). Rotating = new pair in both, restart both.
|
||||
|
||||
Logout ("Sair do Fiscal") ends only the Hub session; coming back through the
|
||||
panel signs in again without a password. There is no distributed single logout.
|
||||
|
||||
Audit (`audit_events`): `SSO_LOGIN_SUCCESS`, `SSO_LOGIN_REJECTED` (with a stable
|
||||
reason: `EXPIRED`, `INVALID_SIGNATURE`, `WRONG_AUDIENCE`, `WRONG_ISSUER`,
|
||||
`REPLAY`, `STATE_MISMATCH`, `DISABLED_IN_FISCAL`, ...), `LOGOUT`,
|
||||
`LOGIN_LOCAL_SUCCESS/FAILURE` — with the real client IP, never the token.
|
||||
|
||||
**Break-glass:** the local admin (`create_admin.py`) still exists for
|
||||
emergencies at `/login/local` (not linked from the UI; every use is audited).
|
||||
Disable a collaborator only in the Hub: `admin_users.is_active = false`.
|
||||
|
||||
## Registering an Agent (per client installation)
|
||||
|
||||
Run once per Agent installation, from anywhere with access to Postgres —
|
||||
|
||||
Reference in New Issue
Block a user