[#1143] Reject expired device codes at the token and verification endpoints - #1144
Merged
vharseko merged 1 commit intoSep 29, 2026
Merged
Conversation
…and verification endpoints The token endpoint checked isAuthorized() before the expiry time, so an approved device code past its lifetime was still exchanged for tokens until the CTS reaper removed it. Check the expiry first, answer expired_token and delete the code, as RFC 8628 section 3.5 requires. The user-code verification page never looked at the expiry either, and isIssued() is never set for device codes, so an expired code could be approved and an approved one re-approved by another session. Treat both as an unknown user code. Fixes OpenIdentityPlatform#1143
maximthomas
approved these changes
Sep 29, 2026
maximthomas
left a comment
Contributor
There was a problem hiding this comment.
praise: Both endpoints now enforce the device code lifetime, and the checks sit where the bug is.
DeviceCodeGrantTypeHandler.handlechecks expiry and deletes the code beforeisAuthorized()(:95-99), so an approved code past its lifetime now getsexpired_token(RFC 8628 §3.5) instead of tokens.- The guard in
DeviceCodeVerificationResource.verify()(:146-150) replaces the deadisIssued()check and sits above therequireConsentsplit, so a consentdecisionPOST cannot act on an expired or already approved code either. DeviceCodeVerificationResourceTestdrives the realverify()against classpath templates, and both rejection cases assert that neither the session validator norupdateDeviceCodeis reached.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes #1143
Problem
Neither side of the device flow enforced the device code lifetime; an expired code stayed usable until the CTS reaper removed it (1 minute by default):
DeviceCodeGrantTypeHandlercheckedisAuthorized()beforegetExpiryTime(), so an approved code past its lifetime was still exchanged for tokens (the "user approved, device resumed polling late" case noted in the Fix Device Code session propagation and OIDC claims #1108 review).DeviceCodeVerificationResource.verify()never checked the expiry, so an expired code — including one that had already been answered withexpired_token— could still be approved.Changes
isAuthorized()branch. An expired code getsexpired_tokenwhether or not it was approved, and is deleted (RFC 8628 §3.5).not_found, like an unknownuser_code. The already-approved check is needed becauseDeviceCode.isIssued()is never set for device codes (setIssued()exists only onAuthorizationCode), so the existing guard never fired and a second session could re-approve a pending code and overwrite its resource owner. The check sits at the top ofverify(), so it also covers the consentdecisionPOST.Tests
DeviceCodeGrantTypeHandlerTest: new test for an approved but expired code (no token minted, code deleted); the expired-code test now also verifies the delete; the two authorized-path tests stub a future expiry.DeviceCodeVerificationResourceTest: a valid code is approved (control), an expired code and an already approved code are rejected withnot_foundand never reach the session validator or the token store update.mvn -pl openam-oauth2 test: 839 tests, 0 failures.