Skip to content

fix(auth): keep PATCH credentials body within server-accepted token keys - #43

Merged
ngjunsiang merged 1 commit into
mainfrom
bugfix/rfc6749-token-compat
Sep 30, 2026
Merged

ngjunsiang merged 1 commit into
mainfrom
bugfix/rfc6749-token-compat

Conversation

@nycomp

@nycomp nycomp commented Sep 30, 2026

Copy link
Copy Markdown
Contributor

Summary

Implements the migration for #40, now that campus #648 is implemented: PRs nyjc-computing/campus#650 + nyjc-computing/campus#654 merged to weekly on 2026-09-30 (verified — weekly head 56ae7b9 is exactly the #654 merge).

What changed

  • campus-suite bumped to weekly head 56ae7b9 (0.1.16) — delivers the RFC 6749-compliant OAuthToken interface to the client via the existing weekly branch pin.
  • Credentials.Provider.User.update() strips the scope string alias from the PATCH body. Campus validates PATCH /credentials bodies via OAuthToken(**payload), which rejects the scope alias that to_resource() now dual-emits with 422 VALIDATION_FAILED — i.e. the model's own to_resource() output was no longer PATCHable. The strip is lossless (scopes carries the same information; scope is just " ".join(scopes)). Tracked upstream in PATCH /credentials token validation rejects OAuthToken.to_resource() output since #650 (scope alias → 422) campus#655.
  • New contract tests (tests/unit/test_oauth_token_contract.py) pin the wire shapes taken from the campus weekly route code: token-endpoint RFC payload deserialization (access_token/token_type/expires_in/scope), token_type case normalisation, legacy-record compat, nested credentials-token deserialization, and the PATCH-body validation contract.

Audit (issue #40 action item 1)

Code path Status
Credentials.Provider.User.update() → token.to_resource() Fixed — scope alias stripped
auth.token() / _exchange_code_for_token() → OAuthToken.from_resource(resp.json()) OK — from_resource accepts RFC keys (#650)
UserCredentials.from_resource (credentials GET/list) OK — verified against weekly shapes
Campus.use_token() → token.access_token OK — alias retained (#648 decision 1)
oauth.poll_for_token (raw dict) OK — unaffected (per #40)
logins.update(expiry_seconds) Out of scope — LoginSession model, not OAuthToken
sessions.new() scopes list body key Out of scope — AuthSession; #648 scope flip is server-internal storage
Credentials.Provider.User.new(scopes, expiry_seconds) stub Unchanged — NotImplementedError stub mirroring the server's current POST /credentials contract; revisit at campus deprecation

Dev-deployment exercise (issue #40 action item 2)

No CLIENT_ID/CLIENT_SECRET in this environment, so the authenticated exercise against the Railway dev deployment could not be run autonomously. Instead, the wire shapes were taken from the campus weekly route code (oauth.py device-grant response; credentials.py update_credentials validation) and pinned in offline contract tests; both dev deployments were confirmed reachable (401 on authenticated routes = alive and enforcing auth). Completing the checklist live is expected green given the tests + route code: run Credentials.Provider.User.update() and UserCredentials deserialization against dev with credentials.

Test plan

  • 79 passed (73 pre-existing + 6 new), Python 3.11.4 venv with campus-suite @ weekly head 56ae7b9

campus #650 (merged to weekly) made OAuthToken.to_resource() dual-emit
the RFC 6749 `scope` string alongside `scopes`, but the PATCH
/credentials endpoint validates the body via OAuthToken(**payload),
which rejects the `scope` alias with 422 VALIDATION_FAILED. Strip the
alias in Credentials.Provider.User.update() so token updates keep
working across the campus #648 compat window.

Also bumps campus-suite to weekly head 56ae7b9 (0.1.16) carrying the
RFC 6749 token interface (#650, #654), and adds contract tests pinning
the token-endpoint and credentials resource wire shapes and the PATCH
validation contract.

Fixes #40
@ngjunsiang
ngjunsiang merged commit c0007a4 into main Sep 30, 2026
2 checks passed
@nycomp
nycomp deleted the bugfix/rfc6749-token-compat branch September 30, 2026 06:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants