spring-projects/spring-security · error · AuthenticationCredentialsNotFoundException
Could not find original Authentication object
Error message
Could not find original Authentication object
What it means
SwitchUserWebFilter's attemptExitUser tries to recover the original (pre-impersonation) Authentication by unwrapping the current 'switched user' token. When the current authentication is not a SwitchUserAuthentication (or carries no source authentication), no original user can be found and a UsernameNotFoundException-based 'Could not find original Authentication object' error is thrown, aborting the exit-user (switch back) flow.
Source
Thrown at web/src/main/java/org/springframework/security/web/server/authentication/SwitchUserWebFilter.java:234
protected @Nullable String getUsername(ServerWebExchange exchange) {
return exchange.getRequest().getQueryParams().getFirst(SPRING_SECURITY_SWITCH_USERNAME_KEY);
}
private @NonNull Mono<Authentication> attemptSwitchUser(Authentication currentAuthentication,
@Nullable String userName) {
Assert.notNull(userName, "The userName can not be null.");
this.logger.debug(LogMessage.format("Attempting to switch to user [%s]", userName));
return this.userDetailsService.findByUsername(userName)
.switchIfEmpty(Mono.error(this::noTargetAuthenticationException))
.doOnNext(this.userDetailsChecker::check)
.map((userDetails) -> createSwitchUserToken(userDetails, currentAuthentication));
}
private @NonNull Authentication attemptExitUser(Authentication currentAuthentication) {
Optional<Authentication> sourceAuthentication = extractSourceAuthentication(currentAuthentication);
if (sourceAuthentication.isEmpty()) {
this.logger.debug("Failed to find original user");
throw noOriginalAuthenticationException();
}
return sourceAuthentication.get();
}
private Mono<Void> onAuthenticationSuccess(Authentication authentication, WebFilterExchange webFilterExchange) {
ServerWebExchange exchange = webFilterExchange.getExchange();
SecurityContextImpl securityContext = new SecurityContextImpl(authentication);
return this.securityContextRepository.save(exchange, securityContext)
.doOnSuccess((v) -> this.logger.debug(LogMessage.format("Switched user to %s", authentication)))
.then(this.successHandler.onAuthenticationSuccess(webFilterExchange, authentication))
.contextWrite(ReactiveSecurityContextHolder.withSecurityContext(Mono.just(securityContext)));
}
private Mono<Void> onAuthenticationFailure(AuthenticationException exception, WebFilterExchange webFilterExchange) {
return Mono.justOrEmpty(this.failureHandler).switchIfEmpty(Mono.defer(() -> {
this.logger.debug("Failed to switch user", exception);
return Mono.error(exception);
})).flatMap((failureHandler) -> failureHandler.onAuthenticationFailure(webFilterExchange, exception));View on GitHub (pinned to 96852e8860)
Solutions
- Only expose the exit-impersonation link when the current principal is a SwitchUserAuthentication
- Handle the exception and redirect the user to the home page instead of the exit URL when not impersonating
- Ensure session persistence keeps the SwitchUserAuthentication across restarts, or re-impersonate explicitly
- Guard with a check of the principal type before linking/invoking exit-user
Example fix
// before: always render exit link
<a href="/logout/impersonate">Exit impersonation</a>
// after
<span th:if="${principal instanceof T(org.springframework.security.web.authentication.switchuser.SwitchUserAuthenticationToken)}">
<a href="/logout/impersonate">Exit impersonation</a>
</span> Defensive patterns
Strategy: type-guard
Validate before calling
boolean isImpersonating = SecurityContextHolder.getContext().getAuthentication()
instanceof SwitchUserAuthenticationToken;
if (!isImpersonating) { /* hide / disable exit-impersonation flow */ } Type guard
static boolean isSwitchedUser(Authentication auth) {
return auth instanceof SwitchUserAuthenticationToken;
} Prevention
- Only render the exit-impersonation link for SwitchUserAuthenticationToken principals
- Remember persistent login mechanisms may lose the switch-user wrapper after restarts
- Handle the error and redirect home rather than looping users into a broken exit URL
When it happens
Trigger: Calling the exit-user URL (default /logout/impersonate or configured switchUserUrl) while the current authentication is NOT an impersonation session — e.g. the user was never switched, the session expired and was re-authenticated normally, or the security context was rebuilt from a persistent login that lost the SwitchUserAuthentication wrapper.
Common situations: Users bookmarking the exit-impersonation URL, application restarts with persistent remember-me that don't preserve switch-user state, manual removal of the principal from the session, or hitting exit-impersonate after already exiting.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- SwitchUserFilter.noCurrentUser
- SwitchUserFilter.noOriginalAuthentication
- RunAsImplAuthenticationProvider.incorrectKey
- Authenticated principal required to operate with ACLs
- CasAuthenticationProvider.incorrectKey
AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10).
Data as JSON: /api/errors/deb31ebe8260c1bf.
Report an issue: GitHub.