Skip to content

Extend JWT format validation to verify and refresh endpoints#790

Merged
seapagan merged 6 commits into
mainfrom
feature/extend-jwt-validation
Dec 28, 2025
Merged

Extend JWT format validation to verify and refresh endpoints#790
seapagan merged 6 commits into
mainfrom
feature/extend-jwt-validation

Conversation

@seapagan

Copy link
Copy Markdown
Owner

This PR extends the JWT format validation helper (introduced in the password recovery feature) to the user verification and token refresh endpoints, providing consistent defense-in-depth security across all JWT-accepting endpoints.

Changes:

  • Add JWT format validation to /verify endpoint (app/resources/auth.py)

    • Validates empty tokens, length (1024 char max), and JWT format
    • Rejects malformed tokens before cryptographic verification
    • Same validation strategy as reset password endpoint
  • Add JWT format validation to /refresh endpoint (app/managers/auth.py)

    • Import is_valid_jwt_format helper
    • Validates refresh token format before jwt.decode()
    • Prevents obviously invalid tokens from expensive crypto operations
  • Add comprehensive integration tests (tests/integration/test_auth_routes.py)

    • test_verify_malformed_jwt_tokens: 8 malformed token scenarios
    • test_refresh_malformed_jwt_tokens: 8 malformed token scenarios
    • Tests special chars, wrong part count, empty parts, length violations
    • Tests URL injection attempts (e.g., part1.part2&admin=true.part3)

Security benefits:

  • Consistent validation across all JWT endpoints
  • Defense-in-depth: Multiple validation layers before token processing
  • Performance: Early rejection of invalid formats (fail fast)
  • Better error messages: "invalid format" detected before crypto errors
  • Attack surface reduction: Prevents malformed input from reaching jwt.decode()

This commit extends the JWT format validation helper (introduced in the
password recovery feature) to the user verification and token refresh
endpoints, providing consistent defense-in-depth security across all
JWT-accepting endpoints.

Changes:
- Add JWT format validation to /verify endpoint (app/resources/auth.py)
  - Validates empty tokens, length (1024 char max), and JWT format
  - Rejects malformed tokens before cryptographic verification
  - Same validation strategy as reset password endpoint

- Add JWT format validation to /refresh endpoint (app/managers/auth.py)
  - Import is_valid_jwt_format helper
  - Validates refresh token format before jwt.decode()
  - Prevents obviously invalid tokens from expensive crypto operations

- Add comprehensive integration tests
(tests/integration/test_auth_routes.py)
  - test_verify_malformed_jwt_tokens: 8 malformed token scenarios
  - test_refresh_malformed_jwt_tokens: 8 malformed token scenarios
  - Tests special chars, wrong part count, empty parts, length
violations
  - Tests URL injection attempts (e.g., part1.part2&admin=true.part3)

Security benefits:
- Consistent validation across all JWT endpoints
- Defense-in-depth: Multiple validation layers before token processing
- Performance: Early rejection of invalid formats (fail fast)
- Better error messages: "invalid format" detected before crypto errors
- Attack surface reduction: Prevents malformed input from reaching
jwt.decode()

Coverage improvements:
- auth.py manager: 36% → 55% coverage
- All 25 auth integration tests pass
- mypy and ruff checks pass
@seapagan seapagan self-assigned this Dec 28, 2025
@seapagan seapagan added the enhancement New feature or request label Dec 28, 2025
@codacy-production

Copy link
Copy Markdown

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
-0.06% (target: -1.00%) 100.00%
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (2eabb89) 1670 1670 100.00%
Head commit (7869d5d) 1677 (+7) 1676 (+6) 99.94% (-0.06%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#790) 7 7 100.00%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

@codacy-production

codacy-production Bot commented Dec 28, 2025

Copy link
Copy Markdown

Coverage summary from Codacy

See diff coverage on Codacy

Coverage variation Diff coverage
+0.00% (target: -1.00%) 100.00%
Coverage variation details
Coverable lines Covered lines Coverage
Common ancestor commit (2eabb89) 1670 1670 100.00%
Head commit (a51eb09) 1676 (+6) 1676 (+6) 100.00% (+0.00%)

Coverage variation is the difference between the coverage for the head and common ancestor commits of the pull request branch: <coverage of head commit> - <coverage of common ancestor commit>

Diff coverage details
Coverable lines Covered lines Diff coverage
Pull request (#790) 9 9 100.00%

Diff coverage is the percentage of lines that are covered by tests out of the coverable lines that the pull request added or modified: <covered lines added or modified>/<coverable lines added or modified> * 100%

See your quality gate settings    Change summary preferences

Updates API reference documentation to document the new JWT format
validation added to /verify and /refresh endpoints, providing
consistency
with the password reset endpoint documentation.

Changes:
- Add "Token Validation" section to /refresh endpoint
  - Documents 1024 char max length
  - Documents JWT format requirements (3 dot-separated base64url parts)
  - Documents empty token rejection
  - Documents malformed token rejection before crypto verification

- Add "Security Features" section to /refresh endpoint
  - Format validation benefits (fast syntactic check)
  - Length validation (resource exhaustion prevention)
  - Defense-in-depth approach
  - Early rejection for better error messages
  - Cryptographic verification details
  - Token type checking (typ="refresh")

- Add "Token Validation" section to /verify endpoint
  - Same validation details as refresh endpoint

- Add "Security Features" section to /verify endpoint
  - Same security features as refresh endpoint
  - Additional note about URL parameter safety

- Update error response documentation for /verify
  - Documents that malformed JWTs return 401 Unauthorized

These documentation updates ensure consistency across all JWT-accepting
endpoints and provide clear guidance on validation behavior and security
features.
Adds unit tests for the JWT format validation logic introduced in
the refresh endpoint, significantly improving test coverage and
ensuring proper validation behavior at the unit level.

Changes:
- Add 6 new unit tests for AuthManager.refresh() validation:
  - test_refresh_malformed_jwt_special_chars: Tests special chars
rejection
  - test_refresh_malformed_jwt_wrong_parts: Tests 2/4 part tokens
  - test_refresh_malformed_jwt_empty_parts: Tests tokens with empty
parts
  - test_refresh_oversized_token: Tests 1024+ char length limit
  - test_refresh_url_injection_attempt: Tests URL injection patterns
  - test_refresh_valid_format_invalid_signature: Tests crypto
verification

Coverage improvements:
- app/managers/auth.py: 36% → 86% (+50%)
- Previously uncovered line 177 (InvalidTokenError handler) now covered
- All 64 tests pass (39 unit + 25 integration)
- mypy and ruff checks pass

Test benefits:
- Unit tests catch edge cases faster than integration tests
- Better exception path coverage (especially InvalidTokenError)
- Validates behavior at the manager level, not just endpoint level
- Tests specific to JWT format validation requirements

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 enhances security by extending JWT format validation to the user verification (/verify) and token refresh (/refresh) endpoints. The validation strategy, previously implemented for password reset, now provides consistent defense-in-depth protection across all JWT-accepting endpoints.

Key Changes:

  • Added pre-validation checks (empty token, length limit, format verification) to /verify and /refresh endpoints before cryptographic operations
  • Implemented comprehensive test suites covering 8+ malformed token scenarios per endpoint, including URL injection attempts
  • Updated API documentation with detailed security features and validation rules

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
app/resources/auth.py Added JWT format validation to /verify endpoint with length and format checks before processing
app/managers/auth.py Added JWT format validation to refresh() method with import of validation helper
tests/unit/test_auth_manager.py Added 6 comprehensive unit tests for refresh endpoint validation covering special chars, wrong parts, empty parts, oversized tokens, URL injection, and invalid signatures
tests/integration/test_auth_routes.py Added 2 integration tests covering 8 malformed token scenarios each for verify and refresh endpoints
docs/reference/api.md Enhanced documentation with token validation rules and security features for both endpoints

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

Comment thread app/managers/auth.py Outdated
Comment thread app/resources/auth.py Outdated
Eliminates magic number duplication by extracting 1024 into
MAX_JWT_TOKEN_LENGTH constant in app/managers/helpers.py.

Changes:
- Add MAX_JWT_TOKEN_LENGTH constant
- Update auth.py and resources/auth.py to use constant
- Update all tests to use MAX_JWT_TOKEN_LENGTH + 1
- Replace manual character string with string.ascii_letters +
string.digits

Benefits: Single source of truth, better maintainability, cleaner code.

All 89 tests pass, mypy and ruff checks pass.

Suggested by GitHub Copilot review.

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 7 out of 7 changed files in this pull request and generated 1 comment.


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

Comment thread app/resources/auth.py Outdated
Moves JWT format validation from /verify route handler to
AuthManager.verify() method to match the pattern used in
AuthManager.refresh().

Changes:
- Add validation to AuthManager.verify() (lines 183-191)
- Remove validation from /verify route handler

Benefits: Consistent architecture, manager owns validation,
better unit testability, defense-in-depth regardless of caller.

All 64 tests pass, coverage remains at 86% for auth manager.

Addresses GitHub Copilot architectural consistency feedback.

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 7 out of 7 changed files in this pull request and generated no new comments.


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

@seapagan seapagan marked this pull request as ready for review December 28, 2025 12:41
@seapagan seapagan marked this pull request as draft December 28, 2025 12:41
Adds test_verify_valid_format_invalid_signature to cover the
jwt.InvalidTokenError exception handler (line 242) that was
previously uncovered.

The test uses a JWT with valid format but invalid cryptographic
signature, ensuring the exception path is exercised.

Coverage: app/managers/auth.py now 100% on modified lines (86% overall).
All 65 tests pass.
@seapagan seapagan marked this pull request as ready for review December 28, 2025 12:50
@seapagan seapagan merged commit 4f87649 into main Dec 28, 2025
17 checks passed
@seapagan seapagan deleted the feature/extend-jwt-validation branch December 28, 2025 12:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants