spring-projects/spring-security · error · BadCredentialsException

RunAsImplAuthenticationProvider.incorrectKey

RunAsImplAuthenticationProvider.incorrectKey

Error message

The presented RunAsUserToken does not contain the expected key

What it means

RunAsImplAuthenticationProvider validates RunAsUserToken instances by comparing the token's key hash with the provider's configured key. If they do not match, the token is not trusted and BadCredentialsException is thrown. This prevents forged run-as identity escalation.

Source

Thrown at access/src/main/java/org/springframework/security/access/intercept/RunAsImplAuthenticationProvider.java:68

@NullUnmarked
@Deprecated
public class RunAsImplAuthenticationProvider implements InitializingBean, AuthenticationProvider, MessageSourceAware {

	protected MessageSourceAccessor messages = SpringSecurityMessageSource.getAccessor();

	@SuppressWarnings("NullAway.Init")
	private @Nullable String key;

	@Override
	public void afterPropertiesSet() {
		Assert.notNull(this.key, "A Key is required and should match that configured for the RunAsManagerImpl");
	}

	@Override
	public Authentication authenticate(Authentication authentication) throws AuthenticationException {
		RunAsUserToken token = (RunAsUserToken) authentication;
		if (token.getKeyHash() != this.key.hashCode()) {
			throw new BadCredentialsException(this.messages.getMessage("RunAsImplAuthenticationProvider.incorrectKey",
					"The presented RunAsUserToken does not contain the expected key"));
		}
		return authentication;
	}

	public String getKey() {
		return this.key;
	}

	public void setKey(String key) {
		this.key = key;
	}

	@Override
	public void setMessageSource(MessageSource messageSource) {
		this.messages = new MessageSourceAccessor(messageSource);
	}

View on GitHub (pinned to 96852e8860)

Solutions

  1. Ensure the exact same key string is set on both RunAsManagerImpl and RunAsImplAuthenticationProvider
  2. Externalize the shared key into a property so both beans reference one value
  3. Verify the authentication provider handling run-as tokens is registered for RunAsUserToken type
  4. Check for stale serialized tokens from a previous key and re-authenticate

Example fix

// before
new RunAsManagerImpl("keyOne", "ROLE_RUNAS");
new RunAsImplAuthenticationProvider("keyTwo");

// after
String key = "shared-secure-key";
new RunAsManagerImpl(key, "ROLE_RUNAS");
new RunAsImplAuthenticationProvider(key);
Defensive patterns

Strategy: validation

Validate before calling

if (runAsToken instanceof RunAsUserToken t && t.getKeyHash() != sharedKey.hashCode()) {
    throw new IllegalStateException("RunAs key mismatch before provider call");
}

Type guard

boolean isTrustedRunAsToken(Authentication auth, String key) {
    return auth instanceof RunAsUserToken t && t.getKeyHash() == key.hashCode();
}

Try / catch

try {
    return provider.authenticate(token);
} catch (BadCredentialsException e) {
    throw new AuthenticationServiceException("RunAs key mismatch", e);
}

Prevention

When it happens

Trigger: authenticate() is given a RunAsUserToken whose getKeyHash() differs from hashCode() of the RunAsManager's configured secure key — typically the RunAsManager and RunAsImplAuthenticationProvider were configured with different keys.

Common situations: Copy-paste configuration where the run-as key differs between the <run-as-manager>/RunAsManagerImpl and the authentication provider; multiple applications sharing a token cache but keys were changed; bean wiring pulling the wrong provider.

Related errors


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