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

  1. Only expose the exit-impersonation link when the current principal is a SwitchUserAuthentication
  2. Handle the exception and redirect the user to the home page instead of the exit URL when not impersonating
  3. Ensure session persistence keeps the SwitchUserAuthentication across restarts, or re-impersonate explicitly
  4. 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

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.

Related errors


AI-assisted analysis of spring-projects/spring-security@96852e8860 (2026-09-10). Data as JSON: /api/errors/deb31ebe8260c1bf. Report an issue: GitHub.