Skip to content

fix(auth): exigir reautenticação para as chaves de identidade OIDC e não importar estado de sessão no import-json - #16491

Merged
diegosouzapw merged 2 commits into
release/v3.8.52from
fix/oidc-settings-reauth
Oct 15, 2026
Merged

diegosouzapw merged 2 commits into
release/v3.8.52from
fix/oidc-settings-reauth

Conversation

@diegosouzapw

@diegosouzapw diegosouzapw commented Oct 13, 2026 •

Copy link
Copy Markdown
Collaborator

Esta PR agora parte do tip de release/v3.8.52, que já tem a correção #16479 para o mesmo advisory: SECURITY_IMPACTING_SETTINGS_KEYS cobre requireLogin, as chaves do bypass manage-scope e oidcEnabled / oidcDisablePasswordLogin / oidcClientSecret, e o import-json remove password e essas chaves. A branch foi refeita sobre o tip, com um commit só. Ela cobre as duas brechas que a #16479 deixou abertas.

Brecha 1: chaves de identidade OIDC sem reautenticação

PATCH /api/settings ainda aceitava sem currentPassword o restante da configuração de identidade OIDC. Com uma sessão do dashboard roubada ou uma chave de escopo manage, e sem saber a senha, dava para:

  • trocar oidcIssuer + oidcClientId e apontar o login do dashboard para um IdP do atacante (o callback valida issuer/audience contra esses mesmos valores);
  • colocar o próprio sub/e-mail em oidcAllowedSubjects;
  • alterar oidcRedirectPath, concatenado à origem (${origin}${redirectPath}): @attacker.example/cb manda o authorization code para outro host;
  • mudar oidcScopes.

O atacante continuava com acesso mesmo depois de a senha ser trocada.

Correção: oidcIssuer, oidcClientId, oidcAllowedSubjects, oidcRedirectPath e oidcScopes entram em SECURITY_IMPACTING_SETTINGS_KEYS. Sem senha o PATCH responde 400 PASSWORD_REQUIRED, com senha errada 401 PASSWORD_MISMATCH, e o import-json passa a removê-las. Um teste confere que toda chave oidc* do updateSettingsSchema (são 8) está no conjunto de reauth, para que uma chave OIDC nova não entre desprotegida.

Brecha 2: import-json reabria sessões encerradas

O import-json ainda gravava currentPassword, sessionsValidAfter e revokedDashboardSessions. Importar um backup anterior a uma troca de senha ou a um logout (com sessionsValidAfter: 0 / revokedDashboardSessions: []) reabria as sessões do dashboard que esses eventos tinham encerrado.

Correção: src/shared/constants/securityImpactingSettings.ts passa a exportar, ao lado de SECURITY_IMPACTING_SETTINGS_KEYS:

  • DASHBOARD_SESSION_STATE_SETTINGS_KEYS: sessionsValidAfter e revokedDashboardSessions. Ficam como literais para o módulo de constantes não importar jose, e um teste amarra os nomes a SESSIONS_VALID_AFTER_SETTING / REVOKED_SESSIONS_SETTING de dashboardSessionToken.ts;
  • IMPORT_STRIPPED_SETTINGS_KEYS: password, currentPassword, todas as chaves de reauth e o estado de sessão. É a única lista que o stripSecurityImpactingSettings() do import-json usa.

docs/architecture/AUTHZ_GUIDE.md documenta as duas regras.

UI: sem impacto. O dashboard não tem formulário de configuração OIDC (só src/app/login/page.tsx lê oidcEnabled/oidcDisablePasswordLogin). Com oidcDisablePasswordLogin ativo, o comportamento é o mesmo que a rota já tinha para as chaves protegidas: a flag recusa só o login por senha, o hash continua salvo e a reauth compara currentPassword com ele. A exceção de cold boot não mudou.

Prova TDD (RED → GREEN)

Testes escritos primeiro e rodados contra o código do tip (9ecddd15):

  • tests/unit/oidc-settings-reauth.test.ts (5 chaves × sem senha → 400 / senha errada → 401 / senha certa → 200, mais "toda chave oidc* protegida", "chave não sensível segue sem senha" e "import-json não grava as chaves OIDC"):
    • RED: tests 18 · pass 6 · fail 12
    • GREEN: tests 18 · pass 18 · fail 0
  • tests/unit/settings-import-json-session-state.test.ts (nomes amarrados às constantes, lista de remoção, cutoff e lista de revogação preservados, e o teste ponta a ponta "sessão que fez logout continua morta depois de importar um backup antigo"):
    • RED: tests 4 · pass 0 · fail 4. No ponta a ponta, o token revogado voltava a verificar e sessionsValidAfter era sobrescrito por 0.
    • GREEN: tests 4 · pass 4 · fail 0

tests/unit/oidc-settings-allowlist-guard.test.ts: os dois casos que aceitam gravar oidcAllowedSubjects agora semeiam um hash de senha e enviam currentPassword. É só alinhamento ao novo gate: nenhuma asserção foi removida ou enfraquecida, e eles continuam exigindo 200 e o valor gravado. Os casos de rejeição (OIDC_ALLOWED_SUBJECTS_REQUIRED) ficaram iguais, porque essa checagem roda antes da reauth.

Validação

  • 36 arquivos de teste relacionados (OIDC, rota de settings, import-json, securityImpactingSettings, dashboardSessionToken, tests/unit/settings/*, tests/unit/api/settings*, api-auth, auth-login-route, local-only-bypass-spawn-denylist da fix(security): harden the LOCAL_ONLY manage-scope bypass deny-list and settings import #16479): tests 346 · pass 346 · fail 0
  • npm run typecheck:core: limpo
  • eslint + prettier nos arquivos alterados: limpos
  • npm run check:docs-all: ok

Crédito: relatado por @123abab123789 (trailer Co-authored-by no commit).

⚠️ base-red inherited: #15306

@github-actions

Copy link
Copy Markdown
Contributor

CI Coverage Report

  • Coverage job: skipped
  • PR test policy: success

Coverage artifact was not available for this run.

@123abab123789

123abab123789 commented Oct 14, 2026 via email

Copy link
Copy Markdown
Contributor

…of import-json

The tip's #16479 put requireLogin, the manage-scope bypass keys and
oidcEnabled / oidcDisablePasswordLogin / oidcClientSecret behind password
re-auth and out of the legacy JSON import. Two gaps remained:

1. The rest of the OIDC identity config was still writable without the
   management password. A stolen dashboard session or manage-scope key could
   repoint dashboard login at an attacker-controlled IdP (oidcIssuer +
   oidcClientId), allowlist its own subject (oidcAllowedSubjects), or send the
   authorization code to another host (oidcRedirectPath "@attacker.example/cb"),
   and keep access after the password rotates.
2. import-json still wrote currentPassword, sessionsValidAfter and
   revokedDashboardSessions, so importing an older backup revived dashboard
   sessions that a password change or a sign-out had already ended.

- Add oidcIssuer, oidcClientId, oidcAllowedSubjects, oidcRedirectPath and
  oidcScopes to SECURITY_IMPACTING_SETTINGS_KEYS (PATCH -> 400
  PASSWORD_REQUIRED / 401 PASSWORD_MISMATCH without the right password).
- Export DASHBOARD_SESSION_STATE_SETTINGS_KEYS and IMPORT_STRIPPED_SETTINGS_KEYS
  next to it; import-json strips the latter (password, currentPassword, every
  re-auth key, the session state).
- Tests: every oidc* schema key is guarded; per-key 400/401/200; import-json
  drops the OIDC keys and the session state; a signed-out session stays dead
  after importing a pre-sign-out backup; the session-state names are pinned to
  the dashboardSessionToken constants.
- Align oidc-settings-allowlist-guard: its accepted writes now send the
  management password (assertions unchanged).

Co-authored-by: 123abab123789 <123abab123789@users.noreply.github.com>
@diegosouzapw
diegosouzapw force-pushed the fix/oidc-settings-reauth branch from 030082f to 82186fd Compare October 14, 2026 16:33
@diegosouzapw diegosouzapw changed the title fix(auth): require password re-auth to change the OIDC issuer and client fix(auth): exigir reautenticação para as chaves de identidade OIDC e não importar estado de sessão no import-json Oct 14, 2026
@diegosouzapw
diegosouzapw merged commit 795c593 into release/v3.8.52 Oct 15, 2026
38 of 50 checks passed
@diegosouzapw
diegosouzapw deleted the fix/oidc-settings-reauth branch October 18, 2026 23:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants