Skip to content

Feat/dpop - #113

Open
Avantol13 wants to merge 28 commits into
masterfrom
feat/dpop
Open

Feat/dpop#113
Avantol13 wants to merge 28 commits into
masterfrom
feat/dpop

Conversation

@Avantol13

@Avantol13 Avantol13 commented Aug 21, 2026

Copy link
Copy Markdown
Contributor

New Features

  • DPoP implementation for clients and servers
  • Improved JWT validation without requiring Flask as a dependency, async support
  • denylist_callback provided as a hook to validation

Breaking Changes

  • core.validate_jwt: issuers renamed to allowed_issuers, now required and
    rejected when empty
    - there is no way to skip the issuer check. aud and
    scope remain required, as before. New purpose and denylist_callback params.
  • core.validate_jwt positional slots past 5 shifted: slot 6 was options, now
    purpose; slot 7 was logger, now options. Move those to keywords.
  • Flask token.validate.validate_jwt now raises ValueError when none of
    OIDC_ISSUER/USER_API/BASE_URL is configured, instead of failing every token
    with JWTError.
  • Exception messages no longer wrap the pyjwt exception (JWTAudienceError reports
    got unknown; JWTExpiredError"token has expired"). Don't match on text.
  • In the new async key path, a kid the issuer does not publish is an error rather
    than falling back to another key.

Bug Fixes

  • token/fastapi.py: the issuer derived from the first request leaked into every
    later request, so one token with a junk iss would 403 all subsequent valid
    tokens for the life of the process. Now per-request.
  • JWK n/e are strictly base64url-decoded. Previously a malformed JWKS silently
    produced a junk key, so every token read as "bad signature" rather than
    "issuer published a bad key".
  • keys.py handles both .well-known JWKS and legacy Fence /jwt/keys formats;
    10s timeout on key discovery; the public-key cache is mutex-guarded.
  • Updated all pre-commit tool versions (black 26.5.1 reformat).

Improvements

  • Replace httpx (no longer developed) with httpx2.
  • Updated all pre-commit tool versions

Dependency updates

  • Added joserfc >=1.7.3 (+ truststore, transitive via httpx2)
  • httpx >=0.23,<1.0httpx2 >=2.9.1; pyjwt >=2.4.0>=2.11.0

Deployment changes

  • New env var config for DPoP: DPOP_SHARED_SECRET - required by any service that mints or verifies DPoP
    nonces, MUST BE THE SAME value across ALL participating services.
  • New config for DPoP: DPOP_NONCE_TTL - optional, seconds, default 300.

@github-actions

Copy link
Copy Markdown

The style in this PR agrees with black. ✔️

This formatting comment was generated automatically by a script in uc-cdis/wool.

@coveralls

coveralls commented Aug 21, 2026

Copy link
Copy Markdown

Coverage Report for CI Build 32497763219

Coverage increased (+20.0%) to 80.286%

Details

  • Coverage increased (+20.0%) from the base build.
  • Patch coverage: No coverable lines changed in this PR.
  • 34 coverage regressions across 4 files.

Uncovered Changes

No uncovered changes found.

Coverage Regressions

34 previously-covered lines in 4 files lost coverage.

File Lines Losing Coverage Coverage
authutils/token/core.py 11 88.54%
authutils/token/keys.py 11 94.44%
authutils/oauth2/client/blueprint.py 9 0.0%
authutils/globals.py 3 0.0%

Coverage Stats

Coverage Status
Relevant Lines: 979
Covered Lines: 786
Line Coverage: 80.29%
Coverage Strength: 0.8 hits per line

💛 - Coveralls

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