
Taskcluster web-server OAuth2 authorization codes are reusable and the exchange handler checks the wrong expiry column
The Taskcluster web-server's OAuth2 token-exchange handler does not consume authorization codes and does not enforce the authorization-code expiry. A leaked authorization code can be replayed to mint additional bridge access tokens for the original user, well past the 10-minute window that RFC 6749 §4.1.2 requires.
Source-to-sink
Two bugs in services/web-server/src/servers/oauth2.js, both in the same handler at lines 165-199:
-
The exchange handler validates
redirect_uriand code existence, mints a bridge access token, and returns — it never deletes or otherwise consumes theauthorization_codesrow. RFC 6749 §4.1.2 requires single-use codes: "The client MUST NOT use the authorization code more than once. If an authorization code is used more than once, the authorization server MUST deny the request..." -
The expiry check at line 66 reads
entry.client_details.expires, which is the requested lifetime of the resulting Taskcluster credentials (capped by the registered OAuth client'smaxExpires, e.g."1 year"in the test config). The authorization-code row's intended 10-minute lifetime (entry.expires, set at line 33 totaskcluster.fromNow('10 minutes')) is never checked at exchange time. Expired codes remain usable until the dailycleanup-expire-auth-codescron deletes them (services/web-server/procs.yml:20-24).