Visitar URL original
Improve device workflow by hekhuisk · Pull Request #954 · oauthlib/oauthlib · GitHub
Skip to content

Improve device workflow - #954

Merged
JonathanHuot merged 9 commits into
oauthlib:masterfrom
hekhuisk:improve-device-workflow
Oct 1, 2026
Merged

JonathanHuot merged 9 commits into
oauthlib:masterfrom
hekhuisk:improve-device-workflow

Conversation

@hekhuisk

Copy link
Copy Markdown
Contributor

Note: This branches from #920 so commits 90d8aa8 and 68b292a are both from that PR and once that PR gets merged, this one will be touching less code.

Cleans up the existing device workflow to make it follow how the authorization code workflow works and the overall structure of the library.

  • Moves validation into the GrantType
  • Validates scopes which it is supposed to do, but was not doing
  • Adds validate_device_code to the request validator for callers to implement
  • Adds missing error handling required by the spec for authorization_pending, slow_down, expired_token, and access_denied.

Resolves #944
Resolves #949

I know the guidelines say one PR for one issue, but these issues are both very closely related and felt like a single chunk of work.

Copilot AI 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.

Pull request overview

This PR refactors the RFC 8628 (Device Authorization Grant) implementation to better align with other OAuth2 grant flows in oauthlib, adding a first-class validator hook for device-code polling, improving RFC 8628 error handling, and centralizing client authentication logic in the grant base class.

Changes:

  • Add RequestValidator.validate_device_code() (RFC 8628) and enforce device-code status/error handling during token polling.
  • Move device authorization request validation into DeviceCodeGrant and add scope validation to the device authorization request path.
  • Introduce GrantTypeBase.validate_client_authentication() and adopt it across multiple grant types; expand tests for device-code and endpoint error cases.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/oauth2/rfc8628/grant_types/test_device_code.py Adds coverage for device-code polling statuses, missing device_code, and scope population behavior.
tests/oauth2/rfc8628/endpoints/test_error_responses.py Adds endpoint-level invalid-scope error expectation for device authorization requests.
oauthlib/oauth2/rfc8628/request_validator.py Introduces the new validate_device_code hook for implementers.
oauthlib/oauth2/rfc8628/grant_types/device_code.py Centralizes device authorization validation, adds device-code status→error mapping, and validates device_code before scope validation.
oauthlib/oauth2/rfc8628/endpoints/device_authorization.py Delegates device authorization request validation to DeviceCodeGrant.
oauthlib/oauth2/rfc6749/request_validator.py Clarifies client-auth method contracts and expands “used by” lists.
oauthlib/oauth2/rfc6749/grant_types/resource_owner_password_credentials.py Switches to the shared validate_client_authentication helper.
oauthlib/oauth2/rfc6749/grant_types/refresh_token.py Switches to the shared validate_client_authentication helper and preserves client_id normalization.
oauthlib/oauth2/rfc6749/grant_types/base.py Adds validate_client_authentication helper used by multiple grant types.
oauthlib/oauth2/rfc6749/grant_types/authorization_code.py Switches to the shared validate_client_authentication helper.
oauthlib/oauth2/init.py Reorders/extends top-level exports for RFC 8628 endpoints/errors.
oauthlib/common.py Adds device_code to the common request parameter set.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>

@auvipy auvipy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

please cross check the suggestions

Copilot AI 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.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated no new comments.

Suppressed comments (2)

tests/oauth2/rfc8628/endpoints/test_error_responses.py:109

  • In this test, Request parses/decodes the form body at initialization (decoded_body / _params). Mutating request.body afterwards does not update request.scope/duplicate_params, so the test may not actually exercise invalid-scope handling for the provided scope value.
    def test_invalid_scope(self):
        request = self.build_request()
        request.body = "client_id=foo&scope=foo"
        self.validator.validate_scopes.return_value = False
        self.assert_request_raises(errors.InvalidScopeError, request)

oauthlib/oauth2/rfc8628/grant_types/device_code.py:173

  • For parity with AuthorizationCodeGrant.validate_token_request (oauth2/rfc6749/grant_types/authorization_code.py:497-499), it would help to log when validate_device_code returns authorized but the validator did not populate request.user and/or request.scopes. Without this, missing population can silently fall back to default scopes later and is harder to debug.
        if status == self.DEVICE_CODE_AUTHORIZED:
            return

Copilot AI 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.

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 1 comment.

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@auvipy
auvipy requested a review from JonathanHuot September 20, 2026 06:25
auvipy
auvipy previously requested changes Sep 20, 2026

@auvipy auvipy left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

might need more work

@hekhuisk
hekhuisk requested a review from auvipy September 21, 2026 20:35
Covers the ServerError raised when validate_device_code returns
DEVICE_CODE_AUTHORIZED without populating request.scopes, which was
previously untested.
@JonathanHuot
JonathanHuot dismissed auvipy’s stale review October 1, 2026 22:25

looks like all changes have been applied

@JonathanHuot JonathanHuot left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

LGTM

@JonathanHuot JonathanHuot added the OAuth2-Provider This impact the provider part of OAuth2 label Oct 1, 2026
@JonathanHuot JonathanHuot added this to the 4.1.0 milestone Oct 1, 2026
@JonathanHuot
JonathanHuot merged commit 1c91d64 into oauthlib:master Oct 1, 2026
14 checks passed
@JonathanHuot JonathanHuot modified the milestones: 4.1.0, 5.0.0 Oct 31, 2026
luketainton pushed a commit to luketainton/luke_rsu that referenced this pull request Nov 6, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [oauthlib](https://github.com/oauthlib/oauthlib) ([changelog](https://github.com/oauthlib/oauthlib/blob/master/CHANGELOG.rst)) | `4.0.0` → `4.0.1` | ![age](https://developer.mend.io/api/mc/badges/age/pypi/oauthlib/4.0.1?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/pypi/oauthlib/4.0.0/4.0.1?slim=true) |

---

### OAuthLib: Bearer token can be retrieved as long as client knows a valid client ID for device workflow
[CVE-2026-49272](https://nvd.nist.gov/vuln/detail/CVE-2026-49272) / [GHSA-3jp2-3mrg-2vxc](GHSA-3jp2-3mrg-2vxc)

<details>
<summary>More information</summary>

#### Details
##### Summary
A 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.

##### Details

[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).

I 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`.

##### PoC
Create 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 oauthlib/oauthlib#919).

Create a client that has a `client_type` of `"public"` and set it's `grant_type` to `"urn:ietf:params:oauth:grant-type:device_code"`.

With this implementation of `RequestValidation.authenticate_client` done, use that validator for your token endpoint. Call the token endpoint with a body of
```
{
  "client_id": <ID of the public client you made>,
  "grant_type": "urn:ietf:params:oauth:grant-type:device_code",
  "device_code": <anything you want since it's not validated>
}
```

You now have a bearer token for the client without a valid `device_code`.

##### Impact
Anyone using the RFC 8628 device workflow in oauthlib that doesn't realize the `device_code` parameter is not being validated.

#### Severity
- CVSS Score: 8.5 / 10 (High)
- Vector String: `CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:C/C:L/I:H/A:N`

#### References
- [https://github.com/oauthlib/oauthlib/security/advisories/GHSA-3jp2-3mrg-2vxc](https://github.com/oauthlib/oauthlib/security/advisories/GHSA-3jp2-3mrg-2vxc)
- [https://github.com/oauthlib/oauthlib/pull/954](https://github.com/oauthlib/oauthlib/pull/954)
- [https://github.com/oauthlib/oauthlib/commit/77a5f1c8ce00da2b0b8c5f6e9cc129920085f114](https://github.com/oauthlib/oauthlib/commit/77a5f1c8ce00da2b0b8c5f6e9cc129920085f114)
- [https://github.com/oauthlib/oauthlib](https://github.com/oauthlib/oauthlib)
- [https://github.com/oauthlib/oauthlib/releases/tag/v4.0.1](https://github.com/oauthlib/oauthlib/releases/tag/v4.0.1)

This data is provided by [OSV](https://osv.dev/vulnerability/GHSA-3jp2-3mrg-2vxc) and the [GitHub Advisory Database](https://github.com/github/advisory-database) ([CC-BY 4.0](https://github.com/github/advisory-database/blob/main/LICENSE.md)).
</details>

---

### Release Notes

<details>
<summary>oauthlib/oauthlib (oauthlib)</summary>

### [`v4.0.1`](https://github.com/oauthlib/oauthlib/releases/tag/v4.0.1): 4.0.1

[Compare Source](oauthlib/oauthlib@v4.0.0...v4.0.1)

#### Security

- **CVE-2026-49272** / GHSA-3jp2-3mrg-2vxc: The device code flow token endpoint did not validate the `device_code` parameter. A request with a known `client_id` and a bogus `device_code` could obtain a bearer token. `DeviceCodeGrant.validate_token_request` now calls `DeviceCodeGrant.validate_device_code()`, which rejects missing or unknown device codes (`invalid_request` / `invalid_grant`) and delegates the code's authorization status to the new `RequestValidator.validate_device_code()` hook, per RFC 8628 Section 3.5. (cherry-picked from [#&#8203;954](oauthlib/oauthlib#954))

#### Upgrade note for device flow integrators

Custom `RequestValidator` implementations using the device flow must now implement `validate_device_code(client_id, code, request)`. Return one of the `DeviceCodeGrant.DEVICE_CODE_*` statuses for known codes, or `None` for an unknown code. When returning `AUTHORIZED`, the validator must also populate `request.user` and `request.scopes` from the stored authorization.

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Enabled.

♻ **Rebasing**: Whenever PR is behind base branch, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4yMjEuMSIsInVwZGF0ZWRJblZlciI6IjQ0LjIyMS4xIiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6WyJzZWN1cml0eSJdfQ==-->

Reviewed-on: https://git.tainton.uk/repos/rsu/pulls/71
Co-authored-by: renovate[bot] <renovate-bot@git.tainton.uk>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

OAuth2-Provider This impact the provider part of OAuth2 Security

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Device Grant Scopes not Validated Add validate_device_authorization_code() to RequestValidator to give DeviceCodeGrant symmetry with Authorization Code Grant

4 participants