fix(auth): end the identity-provider session on sign-out and force re-authentication on sign-in #27

Merged
james.maes merged 1 commits from fix/signout-idp-session into main 2026-11-09 20:10:28 +00:00
Owner

James reported: after signing out and restarting the scanner app, the session is "still active" and logs him back in.

Findings, in the order checked:

  1. Real. Ordinary Sign out was documented and coded as local plus QQQ-session only; provider (Authentik) logout lived behind a separate "Sign out of company" action. The provider session in the browser survived, and the next "Sign in with organization" re-authenticated without a password. docs/AUTHENTICATION.md already records that prompt=login alone silently reused retained SSO on real Authentik. The PR 17 ordering fix (provider logout before device-session end) is present and correct, but only the separate company action used it.
  2. Not a cause. Sign-out clears the stored session id and name before and after the server call (noBackupFilesDir store); a restarted ViewModel finds none and does not call the server. New test restartAfterSignOutDoesNotRestoreTheSession covers ordinary and provider sign-out (passes before and after, it is a regression guard).
  3. Not a cause. Session resume is by the stored session id only; there is no resume-by-device-id path, and the HTTP client has no cookie jar.

Changes:

  • Sign out (Account dialog and the pending-approval screen) now runs the existing, verified provider end-session flow when the host enables it and the server advertises nativeSessionLogout; otherwise it stays QQQ-only as before. The separate "Sign out of company" button is folded into Sign out. The provider flow clears local state first, then opens the browser, as before.
  • Every browser sign-in sends max_age=0 next to prompt=login and the existing S256 PKCE and nonce, so a lingering provider session cannot silently sign the next user in. The callback validation is unchanged.
  • No security behaviour is weakened.

Tests (QViewModelLogoutTest): restartAfterSignOutDoesNotRestoreTheSession, signOutUsesProviderLogoutWhenSupported, authorizationRequestForcesReauthentication. The last two reference new symbols, so they fail to compile before the change and pass after. Existing logout-ordering tests still pass. Core 60, mobileapp 65, scanner-host 125 unit tests, 0 failures; lintRelease 0 errors 0 warnings; source standards pass.

Not verified: real Authentik behaviour of max_age=0 and the browser end-session on a device. Qualify on a handheld (sign out, restart, Sign in must ask for the password).

CI note: the hosted emulator step is red for a host reason (KVM group); merging with admin rights.

James reported: after signing out and restarting the scanner app, the session is "still active" and logs him back in. Findings, in the order checked: 1. Real. Ordinary **Sign out** was documented and coded as local plus QQQ-session only; provider (Authentik) logout lived behind a separate "Sign out of company" action. The provider session in the browser survived, and the next "Sign in with organization" re-authenticated without a password. docs/AUTHENTICATION.md already records that prompt=login alone silently reused retained SSO on real Authentik. The PR 17 ordering fix (provider logout before device-session end) is present and correct, but only the separate company action used it. 2. Not a cause. Sign-out clears the stored session id and name before and after the server call (noBackupFilesDir store); a restarted ViewModel finds none and does not call the server. New test restartAfterSignOutDoesNotRestoreTheSession covers ordinary and provider sign-out (passes before and after, it is a regression guard). 3. Not a cause. Session resume is by the stored session id only; there is no resume-by-device-id path, and the HTTP client has no cookie jar. Changes: - Sign out (Account dialog and the pending-approval screen) now runs the existing, verified provider end-session flow when the host enables it and the server advertises nativeSessionLogout; otherwise it stays QQQ-only as before. The separate "Sign out of company" button is folded into Sign out. The provider flow clears local state first, then opens the browser, as before. - Every browser sign-in sends max_age=0 next to prompt=login and the existing S256 PKCE and nonce, so a lingering provider session cannot silently sign the next user in. The callback validation is unchanged. - No security behaviour is weakened. Tests (QViewModelLogoutTest): restartAfterSignOutDoesNotRestoreTheSession, signOutUsesProviderLogoutWhenSupported, authorizationRequestForcesReauthentication. The last two reference new symbols, so they fail to compile before the change and pass after. Existing logout-ordering tests still pass. Core 60, mobileapp 65, scanner-host 125 unit tests, 0 failures; lintRelease 0 errors 0 warnings; source standards pass. Not verified: real Authentik behaviour of max_age=0 and the browser end-session on a device. Qualify on a handheld (sign out, restart, Sign in must ask for the password). CI note: the hosted emulator step is red for a host reason (KVM group); merging with admin rights.
james.maes added 1 commit 2026-11-09 20:10:08 +00:00
fix(auth): end the identity-provider session on sign-out and force re-authentication on sign-in
android / android (push) Failing after 3m53s
build / android (push) Failing after 3m53s
b87171cafc
Ordinary Sign out cleared only the QQQ session; the provider session left in the
browser silently signed the same person back in (prompt=login alone was already
observed reusing retained SSO on real Authentik). Sign out now runs the provider
end-session flow when the server supports it, and every sign-in also sends
max_age=0. Stored-session restore after sign-out was checked: not a cause.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
james.maes merged commit 9bae976138 into main 2026-11-09 20:10:28 +00:00
Sign in to join this conversation.