{"record":{"id":"e71a3477fa34cafa","repo":"apereo/cas","slug":"request-is-assigned-an-invalid-device-identifier","errorCode":null,"errorMessage":"Request is assigned an invalid device identifier","messagePattern":"Request is assigned an invalid device identifier","errorType":"exception","errorClass":"AuthenticationException","httpStatus":null,"severity":"error","filePath":"support/cas-server-support-qr-authentication/src/main/java/org/apereo/cas/qr/validation/DefaultQRAuthenticationTokenValidatorService.java","lineNumber":66,"sourceCode":"\n        val authentication = tgt.getAuthentication();\n        LOGGER.trace(\"Authentication attempt linked to [{}] is [{}]\", tgt.getId(), authentication);\n\n        if (!authentication.getPrincipal().getId().equals(claims.getSubject())) {\n            val message = String.format(\"Token %s does not belong to the assigned principal\", claims.getSubject());\n            throw new AuthenticationException(message);\n        }\n\n        if (!claims.getIssuer().equals(casProperties.getServer().getPrefix())) {\n            val message = String.format(\"Token %s has an invalid issuer %s that does not match %s\", tgt.getId(),\n                claims.getIssuer(), casProperties.getServer().getPrefix());\n            throw new AuthenticationException(message);\n        }\n\n        val tokenDeviceId = FunctionUtils.doUnchecked(() -> claims.getStringClaim(QRAuthenticationConstants.QR_AUTHENTICATION_DEVICE_ID));\n        if (!Strings.CI.equals(tokenDeviceId, request.getDeviceId())) {\n            LOGGER.warn(\"Request device identifier [{}] does not match the token's identifier: [{}]\", request.getDeviceId(), tokenDeviceId);\n            throw new AuthenticationException(\"Request is assigned an invalid device identifier\");\n        }\n\n        if (!deviceRepository.isAuthorizedDeviceFor(request.getDeviceId(), claims.getSubject())) {\n            val message = String.format(\"Token is not authorized for device identifier [%s]\", request.getDeviceId());\n            throw new AuthenticationException(message);\n        }\n\n        return QRAuthenticationTokenValidationResult.builder()\n            .authentication(authentication)\n            .build();\n    }\n}\n","sourceCodeStart":48,"sourceCodeEnd":79,"githubUrl":"https://github.com/apereo/cas/blob/e7288fc434b4f4505b8452e1a57e8fb3111bb863/support/cas-server-support-qr-authentication/src/main/java/org/apereo/cas/qr/validation/DefaultQRAuthenticationTokenValidatorService.java#L48-L79","documentation":"AuthenticationException thrown by DefaultQRAuthenticationTokenValidatorService.validate when the device id claim (QR_AUTHENTICATION_DEVICE_ID) in the QR token does not match the device id presented in the validation request. The token is bound to the device that originally requested the QR code.","triggerScenarios":"Strings.CI.equals(tokenDeviceId, request.getDeviceId()) is false during QR token validation — request.getDeviceId() differs from the device id embedded in the JWT at mint time.","commonSituations":"User scans the QR code with a different device/browser profile than the one that displayed it; device id regenerated after cookies/storage cleared; multiple devices sharing a session with stale QR codes; device id not passed through correctly by the client integration.","solutions":["Display a fresh QR code on the requesting device and scan it from that same device/session.","Ensure the client persists and consistently sends the same device id between QR generation and validation.","Verify no middleware/browser privacy settings regenerate the device identifier mid-flow.","Check that QR generation and validation use the same device id source (cookie/local storage key) in custom frontends."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"// Ensure the request carries the same device id as the token\nString tokenDeviceId = claims.getClaim(QR_AUTHENTICATION_DEVICE_ID, String.class);\nif (tokenDeviceId != null && !tokenDeviceId.equalsIgnoreCase(requestDeviceId)) {\n    return error(\"device mismatch; regenerate QR code\");\n}","typeGuard":null,"tryCatchPattern":"try { validatorService.validate(request); } catch (AuthenticationException e) { if (e.getMessage().contains(\"invalid device identifier\")) { return regenerateQrEvent(); } throw e; }","preventionTips":["Persist the device id stably (cookie/localStorage) across the QR flow.","Generate the QR code and validate on the same device/session.","Avoid privacy tooling or middleware that rotates device identifiers mid-session."],"tags":["qr-authentication","device-binding","jwt"],"backgroundTag":"invalid-argument-value","analyzedSha":"e7288fc434b4f4505b8452e1a57e8fb3111bb863","analyzedAt":"2026-09-08T15:39:16.015Z","contentChangedAt":"2026-09-08T15:39:16.015Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}