spring-projects/spring-security · warning · SessionAuthenticationException
ConcurrentSessionControlAuthenticationStrategy.exceededAllowed
ConcurrentSessionControlAuthenticationStrategy.exceededAllowed
Error message
Maximum sessions of {0} for this principal exceeded What it means
ConcurrentSessionControlAuthenticationStrategy enforces a maximum number of simultaneous sessions per principal. When the limit is exceeded and exceptionIfMaximumExceeded is true (or the session list is null), it throws SessionAuthenticationException with the allowed count. This is the 'reject login instead of evicting oldest session' mode.
Solutions
- Set exceptionIfMaximumExceeded(false) to evict the least-recently-used session instead of rejecting the login
- Raise maximumSessions to a realistic value for your users' device usage
- Configure a distributed SessionRegistry (e.g. Spring Session backed) so stale sessions are cleaned across nodes
- Educate users or provide a 'sign out other sessions' flow
Example fix
// before
http.sessionManagement(s -> s.maximumSessions(1)
.maxSessionsPreventsLogin(true)); // rejects new logins
// after
http.sessionManagement(s -> s.maximumSessions(1)
.maxSessionsPreventsLogin(false)); // evicts oldest session instead Defensive patterns
Strategy: try-catch
Validate before calling
SessionInformation[] active = sessionRegistry.getAllSessions(principal, false);
if (active != null && active.length >= maximumSessions && preventLogin) {
// warn user or force a logout of other sessions before authenticating
return;
} Try / catch
try {
authenticationManager.authenticate(token);
strategy.onAuthentication(authentication, request, response);
} catch (SessionAuthenticationException e) {
request.setAttribute("maxSessionsExceeded", true);
response.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());
} Prevention
- Choose maxSessionsPreventsLogin deliberately; default evicts oldest session
- Back the SessionRegistry with Spring Session for distributed deployments
- Clean stale sessions after restarts or set proper session TTLs
- Detect shared-credential patterns and alert rather than only rejecting
When it happens
Trigger: onAuthentication -> allowableSessionsExceeded is reached when the user authenticates while already holding maximumSessions sessions and exceptionIfMaximumExceeded=true; also thrown if the registry returns a null session list.
Common situations: maximumSessions configured (e.g. 1) with exceptionIfMaximumExceeded(true) and users sharing credentials across devices; session registry out of sync after app restart leaving stale sessions; load-balanced deployment without sticky/replicated sessions.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- A ReactiveSessionRegistry is needed for concurrent session…
- AccountStatusUserDetailsChecker.disabled
- AccountStatusUserDetailsChecker.expired
- An Authentication object was not found in the…
- Authenticated principal required to operate with ACLs
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/de1ac7d05fe5b2fd.
Report an issue: GitHub.
Appendix: source
Thrown at web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java:151
* @return either -1 meaning unlimited, or a positive integer to limit (never zero)
*/
protected int getMaximumSessionsForThisUser(Authentication authentication) {
return this.sessionLimit.apply(authentication);
}
/**
* Allows subclasses to customise behaviour when too many sessions are detected.
* @param sessions either <code>null</code> or all unexpired sessions associated with
* the principal
* @param allowableSessions the number of concurrent sessions the user is allowed to
* have
* @param registry an instance of the <code>SessionRegistry</code> for subclass use
*
*/
protected void allowableSessionsExceeded(List<SessionInformation> sessions, int allowableSessions,
SessionRegistry registry) throws SessionAuthenticationException {
if (this.exceptionIfMaximumExceeded || (sessions == null)) {
throw new SessionAuthenticationException(
this.messages.getMessage("ConcurrentSessionControlAuthenticationStrategy.exceededAllowed",
new Object[] { allowableSessions }, "Maximum sessions of {0} for this principal exceeded"));
}
// Determine least recently used sessions, and mark them for invalidation
sessions.sort(Comparator.comparing(SessionInformation::getLastRequest));
int maximumSessionsExceededBy = sessions.size() - allowableSessions + 1;
List<SessionInformation> sessionsToBeExpired = sessions.subList(0, maximumSessionsExceededBy);
for (SessionInformation session : sessionsToBeExpired) {
session.expireNow();
}
}
/**
* Sets the <tt>exceptionIfMaximumExceeded</tt> property, which determines whether the
* user should be prevented from opening more sessions than allowed. If set to
* <tt>true</tt>, a <tt>SessionAuthenticationException</tt> will be raised which means
* the user authenticating will be prevented from authenticating. if set to
* <tt>false</tt>, the user that has already authenticated will be forcibly loggedView on GitHub (pinned to 96852e8860)