Skip to content

build: move to django-oauth-toolkit 3.4.1 - #822

Merged
cigamit merged 1 commit into
ctrliq:mainfrom
blaipr:build/django-oauth-toolkit-3.4.1
Sep 7, 2026
Merged

cigamit merged 1 commit into
ctrliq:mainfrom
blaipr:build/django-oauth-toolkit-3.4.1

Conversation

@blaipr

@blaipr blaipr commented Sep 6, 2026

Copy link
Copy Markdown
Contributor
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 RefreshToken row in place. It does. RefreshToken.revoke() still sets a revoked timestamp and saves.

What actually changed is __str__. 3.4.1 stopped rendering the secret in Grant, AccessToken and RefreshToken, on the grounds that __str__ reaches admin breadcrumbs, tracebacks and log output. Three lookups in the tests were written as filter(token=refresh_token), passing the model instance where a CharField belongs, and only ever matched because __str__ used to return the token. Correcting those three to .token makes the existing suite pass with no other change.

ISSUE TYPE
  • Bug, Docs Fix or other nominal change
COMPONENT NAME
  • API
ADDITIONAL INFORMATION

The migration, read as SQL rather than as an operation list. 0213 looks alarming at nineteen operations, but sqlmigrate shows the whole of it:

ALTER TABLE "main_oauth2accesstoken" ADD COLUMN "resource" jsonb DEFAULT '[]'::jsonb NOT NULL;
ALTER TABLE "main_oauth2application" ADD COLUMN "cimd_expires_at" timestamp with time zone NULL;
ALTER TABLE "main_oauth2application" ADD COLUMN "registration_source" varchar(32) DEFAULT 'manual' NOT NULL;
ALTER TABLE "main_oauth2application" ALTER COLUMN "client_id" TYPE varchar(255);

Three additive columns with defaults, and a widening. The other seventeen operations attach verbose_name and 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:

before after
access tokens 71 71, every token value intact
resource absent present, [] on every row, no NULLs
client_id width 100 255

The rollback to 0212 was exercised too: it unapplies cleanly and the 71 rows survive. Worth knowing that the rollback narrows client_id back to varchar(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 through str() or repr(). The second fails on 3.3.0, which is the point of it.

Nothing in awx stringifies these objects, and OAuth2Application keeps the __str__ from AbstractApplication, which still returns the name.

One new warning is deliberately left visible. oauth2_provider.W011 arrives with the release and reports that main.OAuth2AccessToken and oauth2_provider.RefreshToken sit in different apps with a circular foreign key. It surfaces only under awx-manage check, not in ordinary command output. Clearing it means swapping RefreshToken into main, which is a data migration and its own piece of work, so the warning is left where it can be seen rather than added to SILENCED_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:

  • Full suite: 3974 passed, 6 skipped.
  • awx-manage check_migrations: no changes detected, so 0213 captures the model state completely.
  • Migration history replay against PostgreSQL: 2 passed.
  • black, flake8 and yamllint clean.

@cigamit cigamit self-assigned this Sep 7, 2026
@cigamit
cigamit requested a lite review from Copilot September 7, 2026 05:04
@cigamit cigamit added dependencies Pull requests that update a dependency file python Pull requests that update python code Needs triage When a Issue needs to be researched or a PR has an issue that needs fixing before merging labels Sep 7, 2026

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.

🔵 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-toolkit to 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
blaipr force-pushed the build/django-oauth-toolkit-3.4.1 branch from 78242a9 to 65be0d1 Compare September 7, 2026 06:43
@cigamit cigamit removed the Needs triage When a Issue needs to be researched or a PR has an issue that needs fixing before merging label Sep 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

dependencies Pull requests that update a dependency file python Pull requests that update python code

Development

Successfully merging this pull request may close these issues.

3 participants