{"record":{"id":"de1ac7d05fe5b2fd","repo":"spring-projects/spring-security","slug":"concurrentsessioncontrolauthenticationstrategy-exc","errorCode":"ConcurrentSessionControlAuthenticationStrategy.exceededAllowed","errorMessage":"Maximum sessions of {0} for this principal exceeded","messagePattern":"Maximum sessions of (.+?) for this principal exceeded","errorType":"exception","errorClass":"SessionAuthenticationException","httpStatus":null,"severity":"warning","filePath":"web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java","lineNumber":151,"sourceCode":"\t * @return either -1 meaning unlimited, or a positive integer to limit (never zero)\n\t */\n\tprotected int getMaximumSessionsForThisUser(Authentication authentication) {\n\t\treturn this.sessionLimit.apply(authentication);\n\t}\n\n\t/**\n\t * Allows subclasses to customise behaviour when too many sessions are detected.\n\t * @param sessions either <code>null</code> or all unexpired sessions associated with\n\t * the principal\n\t * @param allowableSessions the number of concurrent sessions the user is allowed to\n\t * have\n\t * @param registry an instance of the <code>SessionRegistry</code> for subclass use\n\t *\n\t */\n\tprotected void allowableSessionsExceeded(List<SessionInformation> sessions, int allowableSessions,\n\t\t\tSessionRegistry registry) throws SessionAuthenticationException {\n\t\tif (this.exceptionIfMaximumExceeded || (sessions == null)) {\n\t\t\tthrow new SessionAuthenticationException(\n\t\t\t\t\tthis.messages.getMessage(\"ConcurrentSessionControlAuthenticationStrategy.exceededAllowed\",\n\t\t\t\t\t\t\tnew Object[] { allowableSessions }, \"Maximum sessions of {0} for this principal exceeded\"));\n\t\t}\n\t\t// Determine least recently used sessions, and mark them for invalidation\n\t\tsessions.sort(Comparator.comparing(SessionInformation::getLastRequest));\n\t\tint maximumSessionsExceededBy = sessions.size() - allowableSessions + 1;\n\t\tList<SessionInformation> sessionsToBeExpired = sessions.subList(0, maximumSessionsExceededBy);\n\t\tfor (SessionInformation session : sessionsToBeExpired) {\n\t\t\tsession.expireNow();\n\t\t}\n\t}\n\n\t/**\n\t * Sets the <tt>exceptionIfMaximumExceeded</tt> property, which determines whether the\n\t * user should be prevented from opening more sessions than allowed. If set to\n\t * <tt>true</tt>, a <tt>SessionAuthenticationException</tt> will be raised which means\n\t * the user authenticating will be prevented from authenticating. if set to\n\t * <tt>false</tt>, the user that has already authenticated will be forcibly logged","sourceCodeStart":133,"sourceCodeEnd":169,"githubUrl":"https://github.com/spring-projects/spring-security/blob/96852e8860138a482cb13d1479573f24ff6443c6/web/src/main/java/org/springframework/security/web/authentication/session/ConcurrentSessionControlAuthenticationStrategy.java#L133-L169","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nhttp.sessionManagement(s -> s.maximumSessions(1)\n        .maxSessionsPreventsLogin(true)); // rejects new logins\n// after\nhttp.sessionManagement(s -> s.maximumSessions(1)\n        .maxSessionsPreventsLogin(false)); // evicts oldest session instead","handlingStrategy":"try-catch","validationCode":"SessionInformation[] active = sessionRegistry.getAllSessions(principal, false);\nif (active != null && active.length >= maximumSessions && preventLogin) {\n    // warn user or force a logout of other sessions before authenticating\n    return;\n}","typeGuard":null,"tryCatchPattern":"try {\n    authenticationManager.authenticate(token);\n    strategy.onAuthentication(authentication, request, response);\n} catch (SessionAuthenticationException e) {\n    request.setAttribute(\"maxSessionsExceeded\", true);\n    response.sendError(HttpServletResponse.SC_CONFLICT, e.getMessage());\n}","preventionTips":["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"],"tags":["spring-security","session","concurrency","authentication"],"backgroundTag":"permission-denied","analyzedSha":"96852e8860138a482cb13d1479573f24ff6443c6","analyzedAt":"2026-09-10T23:25:23.477Z","contentChangedAt":"2026-09-10T23:25:23.477Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}