apereo/cas · error · AuthenticationException

Token is not authorized for device identifier

Error message

Token is not authorized for device identifier [%s]

What it means

Thrown by DefaultQRAuthenticationTokenValidatorService.validate when the validated device repository says the device identifier carried in the QR token request is not an authorized device for the token's subject (claims.getSubject()). The token itself is valid, but the requesting device is not linked to that user, so CAS refuses the authentication. This is a device-trust/registration check, not a token signature check.

Solutions

  1. Register/authorize the device for the user via the device repository before submitting the QR token (re-run the device registration flow)
  2. Verify the deviceId sent by the client exactly matches the one bound to the user at registration time (no case/whitespace drift)
  3. Check the device repository backend (Redis/DB) has not been flushed or migrated and still holds the device-to-user authorization
  4. Re-authenticate the QR session end-to-end if the device was intentionally revoked

Example fix

// before: polling with an unregistered device id
val token = obtainToken();
validatorService.validate(QRAuthenticationTokenValidationRequest.builder().token(token).deviceId("unknown-device").build());
// after: use the deviceId returned at device registration
val token = obtainToken();
validatorService.validate(QRAuthenticationTokenValidationRequest.builder().token(token).deviceId(registeredDevice.getId()).build());
Defensive patterns

Strategy: validation

Validate before calling

if (!deviceRepository.isAuthorizedDeviceFor(request.getDeviceId(), claims.getSubject())) {
    // skip token submission / force device re-registration first
    return;
}
validatorService.validate(request);

Type guard

boolean isDeviceAuthorized(QRAuthenticationTokenValidationRequest req, Authentication auth) {
    return req.getDeviceId() != null
        && deviceRepository.isAuthorizedDeviceFor(req.getDeviceId(), auth.getPrincipal().getId());
}

Try / catch

try {
    validatorService.validate(request);
} catch (AuthenticationException e) {
    // prompt device re-registration flow
    logger.warn("Device not authorized for QR auth: {}", request.getDeviceId());
}

Prevention

When it happens

Trigger: A QR authentication token is presented with request.getDeviceId() that is valid (matches tokenDeviceId) but deviceRepository.isAuthorizedDeviceFor(deviceId, subject) returns false — i.e. the device was never registered/authorized for the authenticated user, or the authorization was revoked.

Common situations: User scans a QR code from a new/unregistered device; device registration data was wiped or changed in the backing store (Redis/JDBC/etc.); deviceId case/whitespace mismatch; revoking a device then continuing to poll with the old session.

Understand the failure class

Background: "You do not have permission" / 403 Forbidden errors: authenticated but not allowed — causes and fixes across open-source libraries — this error's family across 31 libraries.

Related errors


AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08). Data as JSON: /api/errors/b5aa239d157e6b1d. Report an issue: GitHub.

Appendix: source

Thrown at support/cas-server-support-qr-authentication/src/main/java/org/apereo/cas/qr/validation/DefaultQRAuthenticationTokenValidatorService.java:71

            val message = String.format("Token %s does not belong to the assigned principal", claims.getSubject());
            throw new AuthenticationException(message);
        }

        if (!claims.getIssuer().equals(casProperties.getServer().getPrefix())) {
            val message = String.format("Token %s has an invalid issuer %s that does not match %s", tgt.getId(),
                claims.getIssuer(), casProperties.getServer().getPrefix());
            throw new AuthenticationException(message);
        }

        val tokenDeviceId = FunctionUtils.doUnchecked(() -> claims.getStringClaim(QRAuthenticationConstants.QR_AUTHENTICATION_DEVICE_ID));
        if (!Strings.CI.equals(tokenDeviceId, request.getDeviceId())) {
            LOGGER.warn("Request device identifier [{}] does not match the token's identifier: [{}]", request.getDeviceId(), tokenDeviceId);
            throw new AuthenticationException("Request is assigned an invalid device identifier");
        }

        if (!deviceRepository.isAuthorizedDeviceFor(request.getDeviceId(), claims.getSubject())) {
            val message = String.format("Token is not authorized for device identifier [%s]", request.getDeviceId());
            throw new AuthenticationException(message);
        }

        return QRAuthenticationTokenValidationResult.builder()
            .authentication(authentication)
            .build();
    }
}

View on GitHub (pinned to e7288fc434)