Repository navigation
build: move to django-oauth-toolkit 3.4.1 - #822
Merged
Merged
Conversation
Contributor
There was a problem hiding this comment.
🔵 Needs a closer look
It includes a third-party auth library upgrade plus a schema migration affecting OAuth token/application tables, which warrants final human review despite the test updates.
Pull request overview
This PR upgrades Ascender’s OAuth dependency (django-oauth-toolkit) to 3.4.1 to pick up security fixes and align the codebase/tests with upstream token model stringification changes. It also introduces the corresponding Django schema migration needed for the updated oauth toolkit model definitions.
Changes:
- Bump
django-oauth-toolkitto 3.4.1 (and adjust the input constraint accordingly). - Fix functional OAuth tests that previously relied on
RefreshToken.__str__returning the token value. - Add new functional coverage for rotated refresh-token replay rejection and for ensuring token secrets are not rendered via
str()/repr().
File summaries
| File | Description |
|---|---|
| requirements/requirements.txt | Pins django-oauth-toolkit to 3.4.1 in the compiled lock file. |
| requirements/requirements.in | Raises the minimum required django-oauth-toolkit version to 3.4.1. |
| awx/main/tests/functional/api/test_oauth.py | Updates token lookup assertions and adds new regression tests for rotation replay + non-leaky stringification. |
| awx/main/migrations/0213_oauth2accesstoken_resource_and_more.py | Adds/adjusts fields to match the updated oauth toolkit models (including new columns and widened client_id). |
Review details
- Files reviewed: 4/4 changed files
- Comments generated: 0
- Review effort level: Lite
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
3.4.0 shipped security fixes, so this was worth scheduling rather than leaving. The reason it had been left is recorded as a rotation change, and that turns out not to be what happens. What actually changed is __str__. 3.4.1 stopped rendering the secret in Grant, AccessToken and RefreshToken, because __str__ reaches admin breadcrumbs, tracebacks and log output. Three test lookups were written as filter(token=refresh_token), passing the model instance where a CharField belongs, and they only ever matched because __str__ used to return the token. They now pass .token. Rotation itself is unchanged: revoke() still sets a revoked timestamp and saves, so the superseded row survives, which is what the surrounding assertions have always claimed. Migration 0213 is what the new release adds to the models. Read as SQL it is three additive columns with defaults, client_id widened from varchar(100) to varchar(255), and seventeen AlterField operations that only attach verbose_name and emit no SQL at all. Two tests are added for behaviour nothing covered. A rotated refresh token replayed against the token endpoint is refused with 400 invalid_grant, and neither token type renders its secret through str() or repr(). Nothing in awx stringifies these objects, and OAuth2Application keeps the old __str__ from AbstractApplication, which returns the name. One new warning arrives with the release and is deliberately left visible. oauth2_provider.W011 reports that main.OAuth2AccessToken and oauth2_provider.RefreshToken sit in different apps with a circular foreign key. It appears only under awx-manage check, and clearing it means swapping RefreshToken into main, which is a data migration rather than part of a bump.
blaipr
force-pushed
the
build/django-oauth-toolkit-3.4.1
branch
from
September 7, 2026 06:43
78242a9 to
65be0d1
Compare
cigamit
approved these changes
Sep 7, 2026
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.
SUMMARY
3.4.0 shipped security fixes, so this was worth scheduling rather than leaving indefinitely.
The recorded reason for leaving it does not hold. It was written down as a rotation change, that 3.4.1 no longer leaves the superseded
RefreshTokenrow in place. It does.RefreshToken.revoke()still sets arevokedtimestamp and saves.What actually changed is
__str__. 3.4.1 stopped rendering the secret inGrant,AccessTokenandRefreshToken, on the grounds that__str__reaches admin breadcrumbs, tracebacks and log output. Three lookups in the tests were written asfilter(token=refresh_token), passing the model instance where aCharFieldbelongs, and only ever matched because__str__used to return the token. Correcting those three to.tokenmakes the existing suite pass with no other change.ISSUE TYPE
COMPONENT NAME
ADDITIONAL INFORMATION
The migration, read as SQL rather than as an operation list.
0213looks alarming at nineteen operations, butsqlmigrateshows the whole of it:Three additive columns with defaults, and a widening. The other seventeen operations attach
verbose_nameand emit no SQL.Applied to real data, not only to a fresh test database. The development database sits at 0212 with 71 access tokens, so a copy of it was migrated:
resource[]on every row, no NULLsclient_idwidthThe rollback to 0212 was exercised too: it unapplies cleanly and the 71 rows survive. Worth knowing that the rollback narrows
client_idback tovarchar(100), so it would fail against an application whose client id exceeded that; the generator produces far shorter ones.Two tests are added for behaviour nothing covered. A rotated refresh token replayed against the token endpoint is refused with exactly
400 invalid_grant, and neither token type renders its secret throughstr()orrepr(). The second fails on 3.3.0, which is the point of it.Nothing in awx stringifies these objects, and
OAuth2Applicationkeeps the__str__fromAbstractApplication, which still returns the name.One new warning is deliberately left visible.
oauth2_provider.W011arrives with the release and reports thatmain.OAuth2AccessTokenandoauth2_provider.RefreshTokensit in different apps with a circular foreign key. It surfaces only underawx-manage check, not in ordinary command output. Clearing it means swappingRefreshTokenintomain, which is a data migration and its own piece of work, so the warning is left where it can be seen rather than added toSILENCED_SYSTEM_CHECKS. That is a call worth disagreeing with: silencing it is a one line change if preferred.Verification, on Python 3.14.7 and Django 6.1.1:
awx-manage check_migrations: no changes detected, so 0213 captures the model state completely.black,flake8andyamllintclean.