Skip to content

[#1143] Reject expired device codes at the token and verification endpoints - #1144

Merged
vharseko merged 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:issue-1143-device-code-expiry
Sep 29, 2026
Merged

vharseko merged 1 commit into
OpenIdentityPlatform:masterfrom
vharseko:issue-1143-device-code-expiry

Conversation

@vharseko

Copy link
Copy Markdown
Member

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):

  • DeviceCodeGrantTypeHandler checked isAuthorized() before getExpiryTime(), 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 with expired_token — could still be approved.

Changes

  • Token endpoint: the expiry check now runs before the isAuthorized() branch. An expired code gets expired_token whether or not it was approved, and is deleted (RFC 8628 §3.5).
  • Verification page: an expired code, or one that has already been approved, is answered with not_found, like an unknown user_code. The already-approved check is needed because DeviceCode.isIssued() is never set for device codes (setIssued() exists only on AuthorizationCode), 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 of verify(), so it also covers the consent decision POST.

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.
  • New DeviceCodeVerificationResourceTest: a valid code is approved (control), an expired code and an already approved code are rejected with not_found and never reach the session validator or the token store update.
  • mvn -pl openam-oauth2 test: 839 tests, 0 failures.

…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
@vharseko vharseko added bug security Security fix or hardening (CVE, GHSA, XSS/CSRF/SSRF) oauth2 OAuth2 / OpenID Connect java Pull requests that update java code tests Test suite: coverage, fixtures, or test infrastructure labels Sep 29, 2026

@maximthomas maximthomas left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

praise: Both endpoints now enforce the device code lifetime, and the checks sit where the bug is.

  • DeviceCodeGrantTypeHandler.handle checks expiry and deletes the code before isAuthorized() (:95-99), so an approved code past its lifetime now gets expired_token (RFC 8628 §3.5) instead of tokens.
  • The guard in DeviceCodeVerificationResource.verify() (:146-150) replaces the dead isIssued() check and sits above the requireConsent split, so a consent decision POST cannot act on an expired or already approved code either.
  • DeviceCodeVerificationResourceTest drives the real verify() against classpath templates, and both rejection cases assert that neither the session validator nor updateDeviceCode is reached.

@vharseko
vharseko merged commit 2d1ea7d into OpenIdentityPlatform:master Sep 29, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug java Pull requests that update java code oauth2 OAuth2 / OpenID Connect security Security fix or hardening (CVE, GHSA, XSS/CSRF/SSRF) tests Test suite: coverage, fixtures, or test infrastructure

Projects

None yet

Development

Successfully merging this pull request may close these issues.

OAuth 2.0 device flow: an expired device code can still be approved and redeemed

2 participants