Skip to content

Add auditors for SSO session idle timeout misconfiguration#232

Open
dasniko wants to merge 2 commits into
iteratec:mainfrom
dasniko:ssoSessionIdleTimeout
Open

Add auditors for SSO session idle timeout misconfiguration#232
dasniko wants to merge 2 commits into
iteratec:mainfrom
dasniko:ssoSessionIdleTimeout

Conversation

@dasniko
Copy link
Copy Markdown
Contributor

@dasniko dasniko commented Mar 27, 2026

Adds two new auditors that check for long SSO session idle timeouts without effective client-level caps.

Realm auditor (SsoSessionIdleTimeoutExceedsClientSessionIdleTimeout):
Flags a realm when ssoSessionIdleTimeout exceeds 1 hour and clientSessionIdleTimeout is either not set or is ≥ the SSO timeout — meaning clients inherit the full long lifetime. Severity scales with the SSO timeout value (Medium / High / Critical).

Client auditor (ClientSessionIdleTimeoutNotSetWhileSsoSessionIdleTimeoutTooLong):
Flags individual OIDC clients that have no client-specific session idle timeout override when the realm already has a long SSO session idle timeout and no realm-level client timeout is configured.

Both auditors also enforce a general rule: a client-related session idle timeout must never be ≥ the realm ssoSessionIdleTimeout, regardless of the absolute value — since this determines the effective refresh token lifetime as well.

AI disclosure: Implemented with support of 🤖 Claude Code

closes #228

dasniko and others added 2 commits March 27, 2026 13:30
…e timeout

Flags realms where ssoSessionIdleTimeout is greater than 3600 seconds
and clientSessionIdleTimeout is either not configured (0) or is greater
than or equal to ssoSessionIdleTimeout, meaning clients inherit or exceed
the long SSO session lifetime without an effective independent cap.
Also flags any realm where clientSessionIdleTimeout is >= ssoSessionIdleTimeout,
regardless of the threshold. The effective idle timeout also determines the
lifetime of refresh tokens.

Severity scales with ssoSessionIdleTimeout:
- Medium: > 3600s (1h)
- High: >= 28800s (8h)
- Critical: >= 86400s (24h)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… session idle timeout is too long

Flags OIDC clients that have no client-specific session idle timeout override
when the realm ssoSessionIdleTimeout exceeds 1 hour and no realm-level
clientSessionIdleTimeout is set, causing the client to silently inherit the
long SSO session lifetime for both sessions and refresh tokens.
Also flags clients whose override is set but is >= the realm ssoSessionIdleTimeout.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@malexmave malexmave added the enhancement New feature or request label Mar 30, 2026
@dasniko
Copy link
Copy Markdown
Contributor Author

dasniko commented Apr 12, 2026

Any feedback and/or opinion on this?

Copy link
Copy Markdown
Collaborator

@malexmave malexmave left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Finally got around to giving this a read. Here's some feedback :)

sso_idle = realm.get_sso_session_idle_timeout()
client_idle = realm.get_client_session_idle_timeout()
if (sso_idle > MAX_SSO_SESSION_IDLE_TIMEOUT_SECONDS and client_idle == 0) or (
client_idle > 0 and client_idle >= sso_idle
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this branch also check that sso_idle > MAX_SSO_SESSION_IDLE_TIMEOUT_SECONDS? Otherwise, it will always fire when client_idle >= sso_idle, even if the sso_idle is sane.

Or was the intent to catch if a client overrides the session lifetime to something higher than the realm, regardless of the specific value? Might also be an interesting auditor, but I'd keep it separate, and it does not match the description above.

Comment on lines +2 to +4
from kcwarden.auditors.realm.sso_session_idle_timeout_exceeds_client_session_idle_timeout import (
MAX_SSO_SESSION_IDLE_TIMEOUT_SECONDS,
)
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Instead of importing this from another auditor, maybe we should pull the logic into a subcheck (see https://github.com/iteratec/kcwarden/blob/main/kcwarden/auditors/subchecks/access_tokens.py for an example).

Comment on lines +60 to +67
def get_sso_session_idle_timeout(self) -> int:
"""Get SSO session idle timeout in seconds."""
return self._d["ssoSessionIdleTimeout"]

def get_client_session_idle_timeout(self) -> int:
"""Get client session idle timeout in seconds. A value of 0 means it is not set (falls back to SSO session idle timeout)."""
return self._d.get("clientSessionIdleTimeout", 0)

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Just flagging here that there is also ssoSessionIdleTimeoutRememberMe that is used if the "Remember me" mode is used. Would be fine with not adding that as part of this pull request, but a long "remember me" time would also lead to very long-lived tokens that might deserve their own auditor.

@dasniko do you want to take this chance to implement that as well while we're at it, or should we put this into a separate issue?

Comment on lines +9 to +13
def make_realm(sso_idle, client_idle):
realm = Mock()
realm.get_sso_session_idle_timeout.return_value = sso_idle
realm.get_client_session_idle_timeout.return_value = client_idle
return realm
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any particular reason why you aren't using the mock_realm fixture?

sso_idle > MAX_SSO_SESSION_IDLE_TIMEOUT_SECONDS
and realm.get_client_session_idle_timeout() == 0
and client_override == 0
) or (client_override > 0 and client_override >= sso_idle):
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Comment from the next auditor on the same functionality also applies here.

Comment on lines +691 to +694
try:
return int(self.get_attributes().get("client.session.idle.timeout", 0))
except ValueError:
return 0
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Intuitively, this seems overly complex - why doesn't a plain self.get_attributes().get("client.session.idle.timeout", 0) work here? If you are worried that get_attributes will return None, we could adapt it to return an empty dict instead if none is set.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

New Auditor: Long running SSO session idle time without client session idle time configured

2 participants