Skip to content

Fix JwtAuthExtension initialization & complete Django 6 migration - #1

Merged
seshagiriprabhu merged 16 commits into
mainfrom
update
Aug 5, 2026
Merged

Fix JwtAuthExtension initialization & complete Django 6 migration#1
seshagiriprabhu merged 16 commits into
mainfrom
update

Conversation

@AswinJ1

@AswinJ1 AswinJ1 commented Jun 9, 2026

Copy link
Copy Markdown

This update addresses critical lifecycle initialization bugs within the Strawberry GraphQL JWT Auth Extension, completes the Django 6 deprecation migration path, and implements modern Python timezone/datetime standards.

##Changes Made

1. Strawberry Extension State Lifecycle Fixes (chowkidar/extension.py)

  • Restored __init__ Initialization: Added back the __init__ constructor and routed it to populate the base state variables (user, _request, _new_jwt_access_token, etc.) upon instantiation via self._request_state(). This prevents the AttributeError: object has no attribute that occurs when Strawberry resolves fields before on_request_start() completes.
  • Defensive Guards in resolve(): Added hasattr checks directly in resolve() to ensure that even if the extension lifecycle is completely bypassed (e.g. during specific complex field resolution schemas or test edge-cases), the authentication variables safely default to None or False.

2. Django 6 Database Constraints (chowkidar/models.py)

  • Deprecated unique_together: Removed the legacy unique_together Meta attribute.
  • Adopted UniqueConstraint: Replaced it with the modern Django UniqueConstraint structure. Because AbstractRefreshToken is an abstract model, string interpolation %(app_label)s_%(class)s_unique_token_revoked was utilized in the constraint name to ensure child classes inherit unique constraint names in the database.

3. Timezone & Datetime Modernization (chowkidar/settings.py, chowkidar/utils/jwt.py)

  • django.utils.timezone.timedelta Removal: Replaced all usages of the deprecated Django timedelta alias with Python's standard from datetime import timedelta.
  • datetime.utcnow() Deprecation: Replaced datetime.utcnow() with the timezone-aware django.utils.timezone.now() to comply with standard Python 3.12+ warnings and Django best practices.
  • JWT Algorithms: Extracted algorithm strings into the centralized JWT_ALGORITHM setting.

4. Package Infrastructure (setup.cfg)

  • Officially expanded package support identifiers. Added classifiers for:
    • Python 3.10 through 3.13
    • Django 4.0 through 6.0

5. Testing Enhancements (tests/test_extension.py)

  • Implemented robust new tests including test_init_sets_state_via_init_after_init and test_resolve_defensive_guards that rigorously verify that context state maintains integrity when edge case execution orders are simulated.
  • The test suite continues to pass at 100% (32/32 tests).

Comment thread chowkidar/utils/jwt.py
@@ -1,5 +1,5 @@
import jwt
from django.utils.timezone import datetime, timedelta

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use Django timezone as it will read the timezone from the client Django application's settings.

from django.utils import timezone

Make execution_context optional in __init__ (strawberry now sets it
after construction), and replace removed on_request_start with the
on_operation generator hook.
- Add ruff.toml for linter/formatter configuration
- Add .pre-commit-config.yaml for pre-commit hook setup
- Add [options.extras_require] dev section in setup.cfg with ruff and pre-commit
- Installable via `pip install -e ".[dev]"`
- Fix trailing whitespace in README.md, LICENSE, and manifest.in
- Reformat setup.py with consistent quoting and multi-line calls
- Bump version to 0.3.3 in setup.cfg to match prior release
- Simplify line wrapping in chowkidar/settings.py
- Clean up test imports (remove unused, sort, split)
- Add missing trailing newline to conftest.py
- Remove unused import in tests/django_settings.py
- Add detailed NumPy-style docstrings with Parameters, Returns, Raises,
  and Notes sections to all public and private functions and classes
- Covers authentication.py, decorators.py, extension.py, models.py,
  view.py, wrappers.py, and all utils modules
- Follow the docstring template established by get_context() in utils/__init__.py
- Includes type annotations and behavioral notes for each symbol
- Linter-driven import and formatting changes included where applicable
- Move get_context() from utils/__init__.py to utils/context.py
- Move validate_email() from utils/__init__.py to utils/validation.py
- Re-export both functions from utils/__init__.py for backward compat
- Keeps the public API unchanged while improving module organization
- Increase test JWT_SECRET_KEY to 50 bytes to satisfy PyJWT's HS256
  minimum key length (RFC 7518 §3.2), eliminating InsecureKeyLengthWarning
- Add TYPE_CHECKING import and annotate return types as AbstractUser
  instead of the dynamic User model reference
- Add pytest, pytest-cov, and parameterized to dev extras
- Configure [tool:pytest] with --cov=chowkidar and term-missing report
- Set testpaths to tests directory
…ge cases

- Rewrite test_extension.py and test_models.py from pytest-style to
  unittest.TestCase for consistency with the rest of the suite
- Add new test files: test_authentication.py, test_decorators.py,
  test_view.py, test_wrappers.py, and utils/ test modules
- Add security edge-case tests across all modules:
  - JWT: alg=none attack, garbage tokens, wrong issuer, empty tokens
  - Email: newline/null-byte/XSS/SQL injection attempts, unicode homograph
  - Auth: empty/whitespace passwords, header injection via email
  - Decorators: falsy userID bypass (False, empty string vs 0, negative)
  - Cookies: Secure, HttpOnly, SameSite=Strict flags, max-age=0 on delete
  - Extension: state leakage between requests, revoked refresh tokens
  - Wrappers: process_request_before_save ordering, missing userID logout
  - Models: token uniqueness, entropy sufficiency, no regen on second save
- Fix wrong-key test to use HS256-compliant key length
- Total: 178 tests, all passing, 100% code coverage (391/391 statements)
- install_requires under [metadata] is ignored by pip; dependencies
  (Django, PyJWT, strawberry-graphql) were not being installed
- Move the declaration to [options] where setuptools actually reads it
- Fixes ModuleNotFoundError for django in CI
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown

☂️ Python Coverage

current status: ✅

Overall Coverage

Lines Covered Coverage Threshold Status
391 391 100% 0% 🟢

New Files

No new covered files...

Modified Files

No covered modified files...

updated for commit: dc5d770 by action🐍

@seshagiriprabhu
seshagiriprabhu merged commit e0e4d22 into main Aug 5, 2026
4 checks passed
@seshagiriprabhu

seshagiriprabhu commented Aug 11, 2026

Copy link
Copy Markdown

Changelog

Security

  • Bump strawberry-graphql to 0.312.3 to apply high-severity security patches
  • Use sufficiently long HMAC key — increase test JWT secret to 50 bytes to satisfy PyJWT's HS256 minimum key length per RFC 7518 §3.2

Bug Fixes

  • Update JWTAuthExtension for newer strawberry-graphql hook pattern — make execution_context optional in __init__ (strawberry now sets it after construction) and replace removed on_request_start with the on_operation generator hook
  • Move install_requires from [metadata] to [options] — dependencies (Django, PyJWT, strawberry-graphql) were not being installed because pip ignores install_requires under [metadata]

Features

  • Add CI workflows — lint and unit testing via GitHub Actions

Refactoring

  • Extract context and validation into separate utils modules — move get_context() to utils/context.py and validate_email() to utils/validation.py; public API unchanged
  • Add type hints to auth functions with AbstractUser return types

Testing

  • Add comprehensive test suite with 100% coverage — 178 tests covering all modules with security edge cases including JWT alg=none attacks, garbage tokens, newline/null-byte/XSS/SQL injection in emails, falsy userID bypass, cookie security flags, state leakage between requests, and more
  • Add pytest-cov and coverage config to setup.cfg

Documentation

  • Add NumPy-style docstrings across all modules with Parameters, Returns, Raises, and Notes sections

Tooling & Chores

  • Upgrade to Django 6 and PyJWT 2.x
  • Add ruff and pre-commit configuration with dev extras installable via pip install -e ".[dev]"
  • Apply ruff formatting and fix whitespace across project
  • Update .gitignore — add VSCode, ai-prompts, and coverage reports

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