{"id":"CVE-2026-49272","kind":"vulnerability","aliases":["CVE-2026-49272","GHSA-3jp2-3mrg-2vxc"],"related":[],"title":"OAuthLib: Bearer token can be retrieved as long as client knows a valid client ID for device workflow","description":"### Summary\nA call to the token endpoint with a grant type of `\"urn:ietf:params:oauth:grant-type:device_code\"` and a bogus `device_code` is able to get a bearer token as long as they know a valid client ID enabled for the device workflow.\n\n### Details\n\n[Section 3.4 of the RFC 8628 spec](https://datatracker.ietf.org/doc/html/rfc8628#section-3.4) says that the token endpoint should take in `device_code` as a required parameter.`DeviceCodeGrant.validate_token_request` does not perform any validation of `device_code`. `DeviceCodeGrant.validate_token_request` should be validating that `device_code` was passed in as a parameter and that it is valid, similar to how `AuthorizationCodeGrant.validate_token_request` does the checks [here](https://github.com/oauthlib/oauthlib/blob/a015b446622f2b925517501fe484c10eff7a83c0/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py#L446) and [here](https://github.com/oauthlib/oauthlib/blob/a015b446622f2b925517501fe484c10eff7a83c0/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py#L483).\n\nI would like to propose a new method be added to `RequestValidator` called `validate_device_code` that is similar to the `validate_code` method and is called inside of `DeviceCodeGrant.validate_token_request`.\n\n### PoC\nCreate an implementation of `RequestValidation.authenticate_client` that returns `True` if the client's `client_type` is `\"public\"`. You would find yourself in this scenario because there's another issue with `DeviceCodeGrant.validate_token_request` where it doesn't properly handle public clients (see https://github.com/oauthlib/oauthlib/issues/919).\n\nCreate a client that has a `client_type` of `\"public\"` and set it's `grant_type` to `\"urn:ietf:params:oauth:grant-type:device_code\"`.\n\nWith this implementation of `RequestValidation.authenticate_client` done, use that validator for your token endpoint. Call the token endpoint with a body of \n```\n{\n  \"client_id\": <ID of the public client you made>,\n  \"grant_type\": \"urn:ietf:params:oauth:grant-type:device_code\",\n  \"device_code\": <anything you want since it's not validated>\n}\n```\n\nYou now have a bearer token for the client without a valid `device_code`.\n\n\n### Impact\nAnyone using the RFC 8628 device workflow in oauthlib that doesn't realize the `device_code` parameter is not being validated.","summary":"OAuthLib's RFC 8628 device-code grant does not check the device_code parameter at the token endpoint. An attacker who knows a client ID enabled for the device workflow can request a bearer token without a valid device code.","severity":"high","cvss":8.5,"epss":null,"exploited":false,"exploited_at":null,"products":[{"name":"oauthlib","fixed":["4.0.0"],"affected":">= 3.3.0, < 4.0.0","ecosystem":"pypi"}],"ecosystems":["pypi"],"tags":["cwe-287","oauth","oauthlib","package","python","self-hosted"],"not_affected_if":["device-code-grant-disabled","oauthlib-4.0.0-or-later","oauthlib-below-3.3.0"],"how_to_check":"- Check the installed version with `pip show oauthlib` and look for a version from `3.3.0` up to but not including `4.0.0`.\n- Search the codebase for `DeviceCodeGrant` or `device_code` to see whether the device-code grant is enabled on a token endpoint.","fix":"- Upgrade `oauthlib` to `4.0.0` or later.\n- Until upgraded, disable the device-code grant type on token endpoints if it is not in use.","status":"open","published_at":"2026-10-27T13:30:17+00:00","first_seen_at":"2026-10-27T13:31:05+00:00","triaged_at":"2026-10-27T13:32:11+00:00","updated_at":"2026-10-27T13:32:11+00:00","category":"package","confidence":0.8,"triage":{"at":"2026-10-27T13:32:11Z","model":"claude-haiku-5-5","output":{"fix":"- Upgrade `oauthlib` to `4.0.0` or later.\n- Until upgraded, disable the device-code grant type on token endpoints if it is not in use.","reach":"internet","title":"OAuthLib: device-code grant issues bearer tokens without validating device_code","impact":"high","summary":"OAuthLib's RFC 8628 device-code grant does not check the device_code parameter at the token endpoint. An attacker who knows a client ID enabled for the device workflow can request a bearer token without a valid device code.","category":"package","nicknames":[],"who_cares":["python","oauth","oauthlib","self-hosted"],"confidence":0.8,"needs_auth":"no","worst_case":"Attacker obtains a bearer token for a device-enabled client","analyst_note":"Confirm which client configurations are required beyond the device grant and check the GHSA page at https://github.com/advisories/GHSA-3jp2-3mrg-2vxc for the exact fixed patch and any workaround.","how_to_check":"- Check the installed version with `pip show oauthlib` and look for a version from `3.3.0` up to but not including `4.0.0`.\n- Search the codebase for `DeviceCodeGrant` or `device_code` to see whether the device-code grant is enabled on a token endpoint.","needs_analyst":true,"not_affected_if":["device-code-grant-disabled","oauthlib-below-3.3.0","oauthlib-4.0.0-or-later"],"who_is_affected":"Applications that use oauthlib 3.3.0 up to 4.0.0 and enable the RFC 8628 device authorization grant on a token endpoint are exposed. The flaw needs a client ID that is enabled for the device workflow, and the sources say nothing more about the client types required beyond a public client in the proof of concept. Services that do not use the device-code grant can skip it.","user_interaction":"no","needs_special_setup":"yes","severity_if_unknown":"high"},"cost_usd":0.000753,"input_tokens":4157,"output_tokens":674,"severity_before":"high"},"analyst_status":null,"analyst_note":null,"search":"'-2026':20A,28A '-287':32B '-49272':21A,29A '/doc/html/rfc8628#section-3.4)':138C '/oauthlib/oauthlib/blob/a015b446622f2b925517501fe484c10eff7a83c0/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py#l446)':196C '/oauthlib/oauthlib/blob/a015b446622f2b925517501fe484c10eff7a83c0/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py#l483).':201C '/oauthlib/oauthlib/issues/919).':279C '2vxc':26A '3.4':130C '3jp2':24A '3mrg':25A '8628':43B,134C,370C 'abl':108C 'add':211C 'anoth':262C 'anyon':366C 'anyth':343C 'attack':60B 'authorizationcodegrant.validate':187C 'bearer':2A,74B,112C,355C 'bodi':326C 'bogus':104C 'call':83C,214C,228C,320C 'check':50B,192C 'client':10A,14A,64B,121C,240C,246C,248C,275C,282C,286C,311C,328C,359C 'code':46B,53B,80B,101C,106C,148C,163C,172C,217C,224C,305C,340C,342C,364C,381C 'creat':235C,280C 'cve':19A,27A 'cwe':31B 'datatracker.ietf.org':137C 'datatracker.ietf.org/doc/html/rfc8628#section-3.4)':136C 'detail':128C 'devic':17A,45B,52B,69B,79B,100C,105C,126C,147C,162C,171C,216C,304C,339C,341C,363C,371C,380C 'device-cod':44B 'devicecodegrant.validate':153C,164C,231C,265C 'doesn':270C,376C 'done':312C 'enabl':66B,123C 'endpoint':58B,87C,143C,319C,323C 'find':254C 'get':110C 'ghsa':23A 'ghsa-3jp2-3mrg-2vxc':22A 'github.com':195C,200C,278C 'github.com/oauthlib/oauthlib/blob/a015b446622f2b925517501fe484c10eff7a83c0/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py#l446)':194C 'github.com/oauthlib/oauthlib/blob/a015b446622f2b925517501fe484c10eff7a83c0/oauthlib/oauth2/rfc6749/grant_types/authorization_code.py#l483).':199C 'github.com/oauthlib/oauthlib/issues/919).':277C 'grant':47B,90C,98C,294C,302C,330C,337C 'grant-typ':97C,301C,336C 'handl':273C 'host':39B 'id':15A,65B,122C,329C 'ietf':94C,298C,333C 'impact':365C 'implement':237C,308C 'insid':229C 'issu':263C 'know':11A,62B,118C 'like':204C 'long':8A,115C 'method':209C,225C 'new':208C 'oauth':33B,96C,300C,335C 'oauthlib':1A,30A,34B,40B,374C 'packag':35B 'param':95C,299C,334C 'paramet':54B,152C,178C,382C 'pass':174C 'perform':158C 'poc':234C 'proper':272C 'propos':206C 'public':251C,274C,289C 'python':36B 'realiz':378C 'request':72B,155C,166C,189C,233C,267C 'requestvalid':213C 'requestvalidation.authenticate':239C,310C 'requir':151C 'retriev':6A 'return':242C 'rfc':42B,133C,369C 'say':139C 'scenario':258C 'section':129C 'see':276C 'self':38B 'self-host':37B 'set':291C 'similar':184C,220C 'sinc':346C 'spec':135C 'summari':81C 'take':145C 'token':3A,57B,75B,86C,113C,142C,154C,165C,188C,232C,266C,318C,322C,356C 'true':243C 'type':91C,99C,249C,287C,295C,303C,331C,338C 'urn':93C,297C,332C 'use':313C,367C 'valid':13A,78B,120C,160C,169C,183C,215C,223C,315C,350C,362C,386C 'want':345C 'without':76B,360C 'workflow':18A,70B,127C,372C 'would':203C,253C","nicknames":[],"mitigations":[],"who_affected":"Applications that use oauthlib 3.3.0 up to 4.0.0 and enable the RFC 8628 device authorization grant on a token endpoint are exposed. The flaw needs a client ID that is enabled for the device workflow, and the sources say nothing more about the client types required beyond a public client in the proof of concept. Services that do not use the device-code grant can skip it.","tier":"B","verified":{},"analyst_revisit_at":null,"first_public_at":"2026-10-27T13:30:17+00:00","kev":null,"cvss_vector":"CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:N","cvss_by":"GitHub","poc_count":0,"sources":[{"source":"ghsa","external_id":"GHSA-3jp2-3mrg-2vxc","url":"https://github.com/advisories/GHSA-3jp2-3mrg-2vxc","severity":"high","published_at":"2026-10-27T13:30:17+00:00","modified_at":"2026-10-27T13:30:19+00:00","first_seen_at":"2026-10-27T13:31:05+00:00","fetched_at":"2026-10-27T13:31:05+00:00","note":null}],"documents":[],"notice":"Free to read at a human pace. An API with keys and an MCP server are in preparation; early access: hello@infrastack.ai"}