Simplify Auth0 JWT validation and remove excessive logging#1499
Simplify Auth0 JWT validation and remove excessive logging#1499
Conversation
Round of cleanup across the JWT/Auth0 surface: - Reduce the very chatty debug logging in the auth modules and stop echoing JWT prefixes into log lines. - Add request timeouts to the Auth0 management-API calls in ``opencontractserver/users/tasks.py``, validate response status before parsing JSON, URL-encode the user id used in the request path, and use ``select_for_update`` when refreshing the cached M2M token to avoid duplicate rows under concurrent workers. - Drop the unused ``Auth0APIToken.auth0_Response`` text column (new migration ``0027_drop_auth0apitoken_response``). - ``Auth0RemoteUserJSONWebTokenBackend`` now propagates JSON-web-token errors to the GraphQL layer instead of silently returning ``None``, matching the REST authenticator. Token-validation exceptions in ``jwt_auth0_decode`` are raised as ``InvalidTokenError`` so ``get_payload`` can convert them uniformly. - ``Auth0AdminBackend.authenticate`` is reduced to a no-op. The admin login view validates the JWT itself and calls ``login(..., backend=)`` directly; only ``get_user`` needs to remain so sessions rehydrate. - ``_parse_boolean_claim`` only accepts JSON booleans and the canonical ``"true"``/``"false"`` strings so misconfigured Actions fail loudly. - WebSocket middleware drops the legacy ``GraphQLJWTTokenAuthMiddleware`` alias and adds a header-vs-query-string note for callers. - Frontend: ``errorLink`` recognises the structured ``JSONWebTokenExpired`` extension code in addition to message-matching; logout clears the ``oc_has_authenticated`` localStorage hint so returning users hit the fast first-visit path; the annotation-image fetcher uses ``Bearer`` to match the rest of the app; the admin login template uses ``sessionStorage`` for its post-login redirect target; noisy ``console.log`` calls in ``index.tsx`` removed. - Drop the deprecated ``CORS_ORIGIN_WHITELIST`` setting (django- cors-headers replaced it with ``CORS_ALLOWED_ORIGINS`` years ago).
Code Review: PR #1499 — Simplify Auth0 JWT validation and remove excessive loggingOverviewThis is a well-motivated cleanup PR that achieves real improvements: tighter security posture, better operational resilience, and meaningfully less noise. The changes are mostly correct and the architecture decisions (fail-closed boolean parsing, no-op Issues to Address🔴
|
Codecov Report❌ Patch coverage is 📢 Thoughts on this report? Let us know! |
…plementation-5TvVx
Code review (claude bot) follow-ups on config/graphql_auth0_auth/utils.py:
- Replace ``assert isinstance(public_key, RSAPublicKey)`` with an explicit
``raise jwt.InvalidTokenError`` so the guard survives ``python -O``.
- Hoist the RSAPublicKey import to module scope (was inside the hot
``jwt_auth0_decode`` path on every token verification).
- Restore an audit-trail INFO log when an Auth0 claim sync flips
``is_staff``/``is_superuser``. Privilege transitions are rate-limited
via ADMIN_CLAIMS_CACHE_TTL, so this isn't noisy.
- Initialise ``user`` in get_auth0_user_from_token to silence static
analysis (no behaviour change; both branches still assign or return).
opencontractserver/users/tasks.py:
- Replace ``get_new_auth0_token.delay().get()`` with ``.run()`` so
``ensure_valid_auth0_token`` no longer dispatches a task to the broker
and blocks the current worker waiting for a free worker to pick it up
(deadlock under prefork-pool saturation).
Tests:
- New opencontractserver/tests/test_user_auth0_tasks.py covers the
simplified Auth0 task pipeline (token fetch, malformed payloads,
timeouts, URL-encoded ``sub`` claims, fail-closed behaviour, two-step
vs three-step chain selection in sync_remote_user). The module gates
task definitions on ``settings.USE_AUTH0`` at import time, so the
test class flips USE_AUTH0=True and reloads the module in
setUpClass / tearDownClass.
- frontend/src/graphql/errorLink.test.ts: new cases for the
``extensions.code in {JSONWebTokenExpired, TOKEN_EXPIRED, tokenExpired}``
branch added in this PR, plus a negative case that confirms unrelated
extension codes don't trigger the reload-on-expiry path.
Code Review — PR #1499: Simplify Auth0 JWT validation and remove excessive loggingOverall this is a well-structured cleanup with several genuine security and reliability improvements. The signal-to-noise ratio across the auth stack is meaningfully better, and the deadlock fix in Correctness / Potential Bugs1. The old code had 2. After removing the
If there is no catch-all 3. with transaction.atomic():
Auth0APIToken.objects.select_for_update().all().delete()
Auth0APIToken.objects.create(...)
Security4. Making 5. The fail-closed design is correct for new setups. However, any existing Auth0 Action template that emits numeric Breaking Changes6. # removed from config/websocket/middleware.py
GraphQLJWTTokenAuthMiddleware = JWTAuthMiddlewareAny settings file, 7. This is the deprecated Minor / Style8. user.last_ip = data.get("last_ip", user.last_ip)This is cleaner than the old 9. The old 10.
11. Replacing Test CoverageThe new One gap: Summary
|
- Cover Bearer Authorization header in useAnnotationImages - Cover localStorage hint clear (and catch fallback) in useNavMenu.requestLogout Brings the frontend patch coverage on PR #1499 above the 80% gate without adding any production-code behaviour changes.
Signed-off-by: JSIV <5049984+JSv4@users.noreply.github.com>
Code Review — PR #1499: Simplify Auth0 JWT validation and remove excessive loggingOverall this is a high-quality, well-scoped PR. The changes address real production problems (worker hangs, race conditions, silent auth downgrades, log noise), the new test suite is thorough, and the commit message and PR description explain the "why" clearly. A few items deserve attention before merge. Strengths
Issues / Requests for Clarification1. Potential inconsistency in
|
Summary
This PR significantly simplifies Auth0 JWT token validation logic and removes excessive debug/info logging throughout the authentication stack. The changes improve code maintainability, reduce noise in logs, and make the authentication flow more straightforward.
Key Changes
Auth0 JWT Validation (
config/graphql_auth0_auth/utils.py)jwt_auth0_decode(): Removed try-catch wrapper and extensive debug logging; now directly returns decoded tokenget_payload(): Consolidated exception handling to catchjwt.InvalidTokenError(which covers DecodeError, InvalidSignatureError, MissingRequiredClaimError, etc.) instead of handling each separatelyBoolean Claim Parsing (
config/graphql_auth0_auth/utils.py)_parse_boolean_claim()now only accepts JSON booleans and canonical "true"/"false" stringsAdmin Claims Sync
sync_admin_claims_from_payload()Auth0 Token Management (
opencontractserver/users/tasks.py)sync_remote_user()andensure_valid_auth0_token()to use a single live token query instead of deleting all tokens and refreshingtransaction.atomic()with row-level locking to prevent race conditions when multiple workers fetch tokens simultaneouslyapply_data_to_user()now uses.get()with defaults instead of direct key accessAuthentication Backends
Auth0RemoteUserJSONWebTokenBackend: Removed excessive logging; now re-raises bothJSONWebTokenExpiredandJSONWebTokenErrorfor proper GraphQL error handlingAuth0AdminBackend: Madeauthenticate()a no-op (onlyget_user()is used for session rehydration); added documentation explaining why credential verification happens in the view, not the backendJWT Utilities (
config/jwt_utils.py)Frontend Changes
JWTtoBearertoken scheme inuseAnnotationImages.tsxfrontend/src/index.tsxerrorLink.tsDatabase & Constants
Auth0APIToken.auth0_Response(duplicated data)TOKEN_LOG_PREFIX_LENGTHconstant that was only used for debug loggingTests
test_admin_auth.pyto reflect stricter boolean parsing (numeric values no longer accepted)Auth0AdminBackend.authenticate()to be a no-opNotable Implementation Details
https://claude.ai/code/session_019RStWzVYsvjGNwkBz5vQrh