spring-projects/spring-security · error

Invalidated device code used by registered client '%s'

Error message

Invalidated device code used by registered client '%s'

What it means

Emitted by OAuth2DeviceCodeAuthenticationProvider when a client attempts to redeem a device code that was issued to a different client. The provider invalidates the device code to prevent further use, saves the authorization, logs this warning, and throws OAuth2AuthenticationException with INVALID_GRANT. This is a cross-client replay defense for the device authorization grant (RFC 8628).

Source

Thrown at oauth2/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProvider.java:133

		if (authorization == null) {
			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
		}

		if (this.logger.isTraceEnabled()) {
			this.logger.trace("Retrieved authorization with device code");
		}

		OAuth2Authorization.Token<OAuth2DeviceCode> deviceCode = authorization.getToken(OAuth2DeviceCode.class);
		Assert.notNull(deviceCode, "deviceCode cannot be null");

		if (!registeredClient.getId().equals(authorization.getRegisteredClientId())) {
			if (!deviceCode.isInvalidated()) {
				// Invalidate the device code given that a different client is attempting
				// to use it
				authorization = OAuth2Authorization.from(authorization).invalidate(deviceCode.getToken()).build();
				this.authorizationService.save(authorization);
				if (this.logger.isWarnEnabled()) {
					this.logger.warn(LogMessage.format("Invalidated device code used by registered client '%s'",
							authorization.getRegisteredClientId()));
				}
			}
			throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT);
		}

		// In https://www.rfc-editor.org/rfc/rfc8628.html#section-3.5,
		// the following error codes are defined:

		// expired_token
		// The "device_code" has expired, and the device authorization
		// session has concluded. The client MAY commence a new device
		// authorization request but SHOULD wait for user interaction before
		// restarting to avoid unnecessary polling.
		if (deviceCode.isExpired()) {
			if (!deviceCode.isInvalidated()) {
				// Invalidate the device code
				authorization = OAuth2Authorization.from(authorization).invalidate(deviceCode.getToken()).build();

View on GitHub (pinned to 96852e8860)

Solutions

  1. Ensure the token request uses exactly the same client_id that started the device authorization flow.
  2. Check client configuration (application.yml / RegisteredClient settings) so the correct client_id is sent in the device token request.
  3. Do not share device codes between applications; run the device flow again for the second client.
  4. If tokens are unexpectedly revoked, restart the device authorization flow for the correct client.

Example fix

// before
String clientId = "wrong-app"; // hardcoded from another service
client.deviceAccessToken(code, clientId);
// after
String clientId = config.getOwnClientId();
client.deviceAccessToken(code, clientId);
Defensive patterns

Strategy: validation

Validate before calling

// verify client identity before redeeming a device code
if (!issuedClientId.equals(this.clientId)) {
  throw new IllegalStateException("device code was issued to a different client; start your own device flow");
}

Try / catch

try {
  TokenResponse r = redeemDeviceCode(deviceCode);
} catch (OAuth2AuthenticationException e) {
  if ("invalid_grant".equals(e.getError().getErrorCode())) {
    // code invalidated (cross-client use): restart the device authorization flow
    startDeviceAuthorization();
  }
}

Prevention

When it happens

Trigger: During the device access token request, the client_id of the caller does not match the registered client that originally received the device code (e.g. a different device code flow instance, a misconfigured client_id, or a code copied to another application).

Common situations: Copy-pasting a verification URL/device code between two apps configured against the same authorization server, an environment mismatch where staging and prod clients share a database, or a typo/wrong client_id in the token request.

Related errors


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