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:
@@ -1 +1,30 @@
|
||||
<!doctype html><html lang="pt-BR"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Login | GTI Fiscal</title><link rel="stylesheet" href="/static/control-panel.css"></head><body class="login-page"><form class="login-card" method="post" action="/login"><p class="eyebrow">GTI FISCAL / CONTROL PANEL</p><h1>Entrar no painel</h1>{% if error %}<div class="alert error">{{ error }}</div>{% endif %}<label for="login-username">Usuário</label><input id="login-username" name="username" autocomplete="username" required><label for="login-password">Senha</label><input id="login-password" name="password" type="password" autocomplete="current-password" required><button class="primary" type="submit">Acessar</button></form></body></html>
|
||||
<!doctype html>
|
||||
<html lang="pt-BR" data-tema="escuro">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>{% if emergency %}Acesso de emergência{% else %}Entrar{% endif %} | GTI Fiscal</title>
|
||||
<link rel="icon" type="image/png" href="/static/logo-gti-256x256.png">
|
||||
<link rel="stylesheet" href="/static/gti-theme.css">
|
||||
<link rel="stylesheet" href="/static/control-panel.css">
|
||||
</head>
|
||||
<body class="login-page">
|
||||
<div class="cartao login-card">
|
||||
<img src="/static/logo-gti-256x256.png" alt="GTI Grupo" class="login-logo">
|
||||
<h1>GTI Fiscal</h1>
|
||||
{% if emergency %}
|
||||
<p class="login-subtitle">Acesso de emergência (conta local). Colaboradores entram pelo Painel de Clientes.</p>
|
||||
{% else %}
|
||||
<p class="login-subtitle">Entre com suas credenciais para acessar o sistema</p>
|
||||
{% endif %}
|
||||
{% if error %}<div class="mensagem danger">{{ error }}</div>{% endif %}
|
||||
<form method="post" action="/login">
|
||||
<label for="login-username">Usuário</label>
|
||||
<input id="login-username" name="username" autocomplete="username" required autofocus>
|
||||
<label for="login-password">Senha</label>
|
||||
<input id="login-password" name="password" type="password" autocomplete="current-password" required>
|
||||
<button type="submit">Entrar</button>
|
||||
</form>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user