Skip to content

[ENG-2691] Replace python-jose with joserfc#7573

Merged
johnewart merged 4 commits intomainfrom
johnewart/ENG-2691
Mar 7, 2026
Merged

[ENG-2691] Replace python-jose with joserfc#7573
johnewart merged 4 commits intomainfrom
johnewart/ENG-2691

Conversation

@johnewart
Copy link
Collaborator

@johnewart johnewart commented Mar 5, 2026

Ticket ENG-2691

Description Of Changes

python-jose, which we use for encrypting tokens, is subject to a CVE that will not be fixed (as stated by the authors and the maintainers of the underlying CVE-related library) so we are replacing it with joserfc which does not use the vulnerable encryption library and instead uses one that is maintained and secure.

Code Changes

  • Swap out library, update uses

Steps to Confirm

No manual tests needed

Pre-Merge Checklist

  • Issue requirements met
  • All CI pipelines succeeded
  • CHANGELOG.md updated
    • Add a db-migration This indicates that a change includes a database migration label to the entry if your change includes a DB migration
    • Add a high-risk This issue suggests changes that have a high-probability of breaking existing code label to the entry if your change includes a high-risk change (i.e. potential for performance impact or unexpected regression) that should be flagged
    • Updates unreleased work already in Changelog, no new entry necessary
  • UX feedback:
    • All UX related changes have been reviewed by a designer
    • No UX review needed
  • Followup issues:
    • Followup issues created
    • No followup issues
  • Database migrations:
    • Ensure that your downrev is up to date with the latest revision on main
    • Ensure that your downgrade() migration is correct and works
      • If a downgrade migration is not possible for this change, please call this out in the PR description!
    • No migrations
  • Documentation:
    • Documentation complete, PR opened in fidesdocs
    • Documentation issue created in fidesdocs
    • If there are any new client scopes created as part of the pull request, remember to update public-facing documentation that references our scope registry
    • No documentation updates required

Summary by CodeRabbit

  • Chores
    • Replaced the underlying JWE library and updated encryption/decryption handling to remediate a CVE (security dependency update) and improve overall token security, preserving existing public APIs.
    • Added a changelog entry documenting the security remediation.
  • Bug Fixes
    • Improved token decryption and error handling to reduce spurious failures during logout, validation, and download/auth flows.

@johnewart johnewart requested review from JadeCara and erosselli March 5, 2026 05:44
@johnewart johnewart requested a review from a team as a code owner March 5, 2026 05:44
@vercel
Copy link
Contributor

vercel bot commented Mar 5, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

2 Skipped Deployments
Project Deployment Actions Updated (UTC)
fides-plus-nightly Ignored Ignored Preview Mar 6, 2026 10:48pm
fides-privacy-center Ignored Ignored Mar 6, 2026 10:48pm

Request Review

@coderabbitai
Copy link

coderabbitai bot commented Mar 5, 2026

📝 Walkthrough

Walkthrough

Replaces python-jose with joserfc across the project: dependency and mypy overrides updated, JWE generation/decryption reimplemented to use joserfc compact APIs and OctKey import, and exception handling updated in token utilities and a user endpoint. No public API signatures changed.

Changes

Cohort / File(s) Summary
Dependency Configuration
pyproject.toml
Replaced python-jose[cryptography]==3.5.0 with joserfc~=1.6.3; updated mypy overrides from jose.* to joserfc.*.
JWE Implementation
src/fides/api/oauth/jwt.py
Reimplemented generate_jwe/decrypt_jwe to use joserfc.jwe compact APIs with OctKey.import_key; added JWT_ENCRYPTION_ALGORITHM = "A256GCM"; handle string/bytes keys and explicit plaintext decoding.
Token Processing & Error Handling
src/fides/api/oauth/utils.py
Switched decryption to decrypt_jwe from jwt module; replaced jose imports/usages and updated exception handling to catch JoseError/ValueError instead of previous JWEError/JWEParseError.
Endpoint Error Handling
src/fides/api/api/v1/endpoints/user_endpoints.py
Updated logout flow to catch JoseError (from joserfc) instead of jose.exceptions.JWEParseError.
Changelog
changelog/7573.yaml
Added entry documenting replacement of python-jose with joserfc for CVE remediation.

Sequence Diagram(s)

sequenceDiagram
    participant Client
    participant Endpoint as UserEndpoint
    participant Utils as OAuthUtils
    participant JWT as JWTModule
    participant KeyLib as joserfc

    Client->>Endpoint: logout request (token)
    Endpoint->>Utils: extract_token_and_load_client(token)
    Utils->>JWT: decrypt_jwe(token, encryption_key)
    JWT->>KeyLib: OctKey.import_key(encryption_key)
    KeyLib-->>JWT: ImportedKey
    JWT->>KeyLib: jwe.decrypt_compact(token, ImportedKey)
    KeyLib-->>JWT: plaintext
    JWT-->>Utils: decoded payload
    Utils-->>Endpoint: client info or error
    Endpoint-->>Client: response
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Poem

🐰 I hop through keys with a careful twitch,
Importing bytes, making compact JWE rich,
From jose to joserfc I bound and sing,
Tokens unwrap — encryption takes wing! ✨

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: replacing python-jose with joserfc library.
Description check ✅ Passed The description includes the ticket reference, clear explanation of the security issue and rationale, code changes summary, and steps to confirm. It follows the template structure.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch johnewart/ENG-2691

Comment @coderabbitai help to get the list of available commands and usage tips.

@johnewart
Copy link
Collaborator Author

I need to check on fidesplus as well, once all the tests are run, to see if there are any changes there that need to happen in lock-step. I don't think there are but I want to be sure.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 5, 2026

Greptile Summary

This PR replaces the unmaintained python-jose library (which carries an unresolved CVE) with joserfc, updating all JWE encrypt/decrypt call-sites and their exception handling. The mechanical swap is straightforward and the JWE compact serialization format (RFC 7516) is the same across both libraries, so existing tokens remain interoperable.

Key observations:

  • The new joserfc API is used correctly: OctKey.import_key + encrypt_compact/decrypt_compact with {"alg": "dir", "enc": "A256GCM"}.
  • extract_token_and_load_client / extract_token_and_load_client_async catch only DecodeError, but extract_payload re-raises the broader JoseError base class. If joserfc surfaces a decryption failure (e.g., MAC mismatch, key size error) as a JoseError subclass other than DecodeError, it will escape uncaught and return a 500 instead of a 403. The rest of the call-sites correctly catch JoseError.
  • The joserfc>=0.12.0 version constraint is open-ended; the project pins all other production dependencies to exact or tightly-bounded versions. The lock file already resolves to 1.6.3, so pinning to that version or a compatible range would be safer for security-critical code.

Confidence Score: 3/5

  • Safe to merge after addressing the DecodeError vs JoseError exception handling gap in the two extract_token_and_load_client functions.
  • The library swap is correct and the JWE format is RFC-standard so token interoperability is preserved. However, narrowing the exception catch from the full JoseError hierarchy to just DecodeError in the two main auth entry-points is a functional regression: non-decode decryption errors will result in 500 responses instead of 403s, which is a security-sensitive code path.
  • src/fides/api/oauth/utils.py — specifically the exception types caught in extract_token_and_load_client (line 447) and extract_token_and_load_client_async (line 520).

Important Files Changed

Filename Overview
src/fides/api/oauth/jwt.py Replaces python-jose JWE encrypt/decrypt calls with joserfc equivalents, converting the key to OctKey and using encrypt_compact/decrypt_compact. Logic is functionally equivalent.
src/fides/api/oauth/utils.py Updates JWE library usage and exception handling; extract_token_and_load_client and its async variant only catch DecodeError, but extract_payload re-raises the broader JoseError base class — a non-DecodeError JoseError will escape uncaught and produce a 500 response instead of a 403.
src/fides/api/api/v1/endpoints/user_endpoints.py Swap of jose.exceptions.JWEParseError to joserfc DecodeError in the logout path is correct and appropriate.
pyproject.toml Replaces python-jose with joserfc; uses an open-ended >= constraint rather than a pinned version, inconsistent with other dependencies in the file.
uv.lock Lockfile correctly updated: python-jose removed, joserfc 1.6.3 added.

Last reviewed commit: 6bb6fda

Comment on lines 447 to 449
except DecodeError as exc:
logger.debug("Unable to parse auth token.")
raise AuthorizationError(detail="Not Authorized for this action") from exc
Copy link
Contributor

Choose a reason for hiding this comment

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

DecodeError too narrow — non-decode JoseError escapes as 500

extract_payload (line 70) catches and re-raises the full JoseError base class. extract_token_and_load_client (and its async twin at line 520) only catches the narrower DecodeError subclass. Any JoseError that is not a DecodeError — for example a key-size/algorithm mismatch raised by joserfc during decryption — will propagate uncaught all the way up and produce an unhandled 500 error instead of a 403.

In python-jose, JWEParseError covered both structural parse failures and ciphertext corruption. In joserfc those failure modes may surface as different JoseError subclasses, so narrowing the catch to only DecodeError is not a safe like-for-like replacement.

The fix is to catch the base JoseError here (and in the async variant), consistent with how the surrounding _get_webhook_jwe_or_error, _get_request_task_jwe_or_error, and validate_download_token functions already handle it.

Suggested change
except DecodeError as exc:
logger.debug("Unable to parse auth token.")
raise AuthorizationError(detail="Not Authorized for this action") from exc
except JoseError as exc:
logger.debug("Unable to parse auth token.")
raise AuthorizationError(detail="Not Authorized for this action") from exc

The same change is needed at line 520 in extract_token_and_load_client_async.

pyproject.toml Outdated
"pyinstrument==4.5.1",
"pymongo[srv]==4.7.3",
"python-jose[cryptography]==3.5.0",
"joserfc>=0.12.0",
Copy link
Contributor

Choose a reason for hiding this comment

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

Open-ended version constraint is inconsistent with project style

All other production dependencies in this file use pinned or tightly bounded constraints (e.g. redis==7.2.0, loguru==0.7.3). Using joserfc>=0.12.0 means any future major version of the library could be pulled in on a fresh install, potentially introducing breaking API changes in security-critical code.

Consider pinning to the version that is already locked (1.6.3), or at minimum constraining to a compatible minor range:

Suggested change
"joserfc>=0.12.0",
"joserfc>=1.6.3,<2",

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/fides/api/api/v1/endpoints/user_endpoints.py (1)

281-286: ⚠️ Potential issue | 🟡 Minor

Catch JoseError for consistency with other extract_payload callers.

The extract_payload function in oauth/utils.py (lines 70–72) catches and re-raises JoseError, the parent exception class. However, this code at line 285 catches only DecodeError, a subclass. While DecodeError handles most decryption failures, other JoseError subclasses (e.g., from invalid key handling) could be raised and remain uncaught. Additionally, elsewhere in oauth/utils.py, the same function is caught as JoseError (lines 150, 178, 222), creating inconsistency.

Change line 285 from except DecodeError: to except JoseError: to align with the broader error handling pattern and ensure all decryption-related failures are handled uniformly.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/fides/api/api/v1/endpoints/user_endpoints.py` around lines 281 - 286,
Change the exception caught around the call to extract_payload(authorization,
CONFIG.security.app_encryption_key) in user_endpoints.py: replace the specific
DecodeError with the broader JoseError so the try/except matches other callers;
locate the try block that assigns token_data via
json.loads(extract_payload(...)) and update the except clause to except
JoseError: to uniformly handle all Jose-related errors.
🧹 Nitpick comments (3)
src/fides/api/oauth/jwt.py (2)

25-43: Same type annotation issue and potential code duplication.

  1. The encryption_key parameter has the same type annotation inconsistency as generate_jwe.

  2. This function has significant overlap with extract_payload in utils.py (lines 59-72). Both decode JWE tokens using the same pattern. Consider consolidating to avoid duplication.

🔍 Potential consolidation

extract_payload in utils.py could potentially call decrypt_jwe from this module:

# In utils.py
from fides.api.oauth.jwt import decrypt_jwe

def extract_payload(jwe_string: str, encryption_key: str) -> str:
    """Given a jwe, extracts the payload and returns it in string form."""
    try:
        return decrypt_jwe(jwe_string, encryption_key)
    except JoseError as e:
        logger.debug("Failed to decrypt JWE: {}", e)
        raise e
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/fides/api/oauth/jwt.py` around lines 25 - 43, The decrypt_jwe function's
encryption_key annotation should match generate_jwe (accept both str and bytes)
and avoid duplicating logic present in utils.extract_payload: change
decrypt_jwe's signature to accept Union[str, bytes] (or similar), handle
conversion to bytes and decryption inside decrypt_jwe, then refactor
utils.extract_payload to call fides.api.oauth.jwt.decrypt_jwe and handle
JoseError/logging there (e.g., catch JoseError in extract_payload and re-raise
after logging) so the JWE decoding logic is centralized in decrypt_jwe.

7-22: Type annotation doesn't reflect actual behavior.

The function signature declares encryption_key: str, but lines 12-16 explicitly handle both str and bytes types. Consider updating the type annotation to reflect this:

♻️ Suggested type annotation update
-def generate_jwe(payload: str, encryption_key: str, encoding: str = "UTF-8") -> str:
+def generate_jwe(payload: str, encryption_key: str | bytes, encoding: str = "UTF-8") -> str:

Or using Union for older Python compatibility:

from typing import Union
def generate_jwe(payload: str, encryption_key: Union[str, bytes], encoding: str = "UTF-8") -> str:
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/fides/api/oauth/jwt.py` around lines 7 - 22, Update the generate_jwe
signature to accept both str and bytes for encryption_key (e.g., use Union[str,
bytes] or str | bytes) because the implementation in generate_jwe explicitly
handles both types when building key_bytes and calling OctKey.import_key; adjust
the type import (typing.Union or use PEP 604 syntax) and update the
encryption_key annotation accordingly so the signature matches the runtime
behavior.
src/fides/api/oauth/utils.py (1)

49-49: Remove unused constant JWT_ENCRYPTION_ALGORITHM.

The JWT_ENCRYPTION_ALGORITHM constant defined at line 49 is not used anywhere in the codebase. The actual encryption algorithm specification is handled separately in jwt.py using _JWT_ENCRYPTION_ALGORITHM. This constant appears to be dead code and should be removed.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/fides/api/oauth/utils.py` at line 49, Remove the dead constant
JWT_ENCRYPTION_ALGORITHM from src/fides/api/oauth/utils.py: it is unused and
duplicates the actual value defined as _JWT_ENCRYPTION_ALGORITHM in jwt.py;
delete the JWT_ENCRYPTION_ALGORITHM line and run tests/linting to ensure no
references remain (search for JWT_ENCRYPTION_ALGORITHM and confirm code uses
_JWT_ENCRYPTION_ALGORITHM in jwt.py).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/fides/api/oauth/utils.py`:
- Around line 59-72: The extract_payload function currently catches JoseError
and re-raises it, which leaves callers that only catch DecodeError (e.g.,
extract_token_and_load_client and extract_token_and_load_client_async) exposed
to other JoseError subclasses; change extract_payload (around OctKey.import_key
and jwe.decrypt_compact usage) to catch JoseError and raise a
jose.exceptions.DecodeError (or the library's DecodeError) from the caught
exception so callers that only handle DecodeError will receive the normalized
exception; preserve the original exception as the cause when raising to keep
debugging info.

---

Outside diff comments:
In `@src/fides/api/api/v1/endpoints/user_endpoints.py`:
- Around line 281-286: Change the exception caught around the call to
extract_payload(authorization, CONFIG.security.app_encryption_key) in
user_endpoints.py: replace the specific DecodeError with the broader JoseError
so the try/except matches other callers; locate the try block that assigns
token_data via json.loads(extract_payload(...)) and update the except clause to
except JoseError: to uniformly handle all Jose-related errors.

---

Nitpick comments:
In `@src/fides/api/oauth/jwt.py`:
- Around line 25-43: The decrypt_jwe function's encryption_key annotation should
match generate_jwe (accept both str and bytes) and avoid duplicating logic
present in utils.extract_payload: change decrypt_jwe's signature to accept
Union[str, bytes] (or similar), handle conversion to bytes and decryption inside
decrypt_jwe, then refactor utils.extract_payload to call
fides.api.oauth.jwt.decrypt_jwe and handle JoseError/logging there (e.g., catch
JoseError in extract_payload and re-raise after logging) so the JWE decoding
logic is centralized in decrypt_jwe.
- Around line 7-22: Update the generate_jwe signature to accept both str and
bytes for encryption_key (e.g., use Union[str, bytes] or str | bytes) because
the implementation in generate_jwe explicitly handles both types when building
key_bytes and calling OctKey.import_key; adjust the type import (typing.Union or
use PEP 604 syntax) and update the encryption_key annotation accordingly so the
signature matches the runtime behavior.

In `@src/fides/api/oauth/utils.py`:
- Line 49: Remove the dead constant JWT_ENCRYPTION_ALGORITHM from
src/fides/api/oauth/utils.py: it is unused and duplicates the actual value
defined as _JWT_ENCRYPTION_ALGORITHM in jwt.py; delete the
JWT_ENCRYPTION_ALGORITHM line and run tests/linting to ensure no references
remain (search for JWT_ENCRYPTION_ALGORITHM and confirm code uses
_JWT_ENCRYPTION_ALGORITHM in jwt.py).

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: d040fcc9-9d20-4de4-acd3-c06215ea8d84

📥 Commits

Reviewing files that changed from the base of the PR and between 8bb7361 and 6bb6fda.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (4)
  • pyproject.toml
  • src/fides/api/api/v1/endpoints/user_endpoints.py
  • src/fides/api/oauth/jwt.py
  • src/fides/api/oauth/utils.py

Comment on lines 59 to 72
def extract_payload(jwe_string: str, encryption_key: str) -> str:
"""Given a jwe, extracts the payload and returns it in string form."""
try:
decrypted_payload = jwe.decrypt(jwe_string, encryption_key)
return decrypted_payload.decode("utf-8")
except exceptions.JWEError as e:
key_bytes = (
encryption_key.encode("utf-8")
if isinstance(encryption_key, str)
else encryption_key
)
key = OctKey.import_key(key_bytes)
result = jwe.decrypt_compact(jwe_string, key)
return result.plaintext.decode("utf-8")
except JoseError as e:
logger.debug("Failed to decrypt JWE: {}", e)
raise e
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Exception handling inconsistency: function raises JoseError but some callers catch DecodeError.

The extract_payload function catches and re-raises JoseError (line 70-72). However, in extract_token_and_load_client (line 447) and extract_token_and_load_client_async (line 520), only DecodeError is caught.

Since DecodeError is a subclass of JoseError, other JoseError variants (e.g., encryption errors, key errors) would propagate uncaught and potentially cause 500 errors instead of proper authorization failures.

🔧 Suggested fix for consistency

Either change extract_payload to only raise DecodeError:

-    except JoseError as e:
+    except DecodeError as e:
         logger.debug("Failed to decrypt JWE: {}", e)
         raise e

Or update the callers to catch JoseError:

# In extract_token_and_load_client (line 447)
-    except DecodeError as exc:
+    except JoseError as exc:

# In extract_token_and_load_client_async (line 520)  
-    except DecodeError as exc:
+    except JoseError as exc:
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def extract_payload(jwe_string: str, encryption_key: str) -> str:
"""Given a jwe, extracts the payload and returns it in string form."""
try:
decrypted_payload = jwe.decrypt(jwe_string, encryption_key)
return decrypted_payload.decode("utf-8")
except exceptions.JWEError as e:
key_bytes = (
encryption_key.encode("utf-8")
if isinstance(encryption_key, str)
else encryption_key
)
key = OctKey.import_key(key_bytes)
result = jwe.decrypt_compact(jwe_string, key)
return result.plaintext.decode("utf-8")
except JoseError as e:
logger.debug("Failed to decrypt JWE: {}", e)
raise e
def extract_payload(jwe_string: str, encryption_key: str) -> str:
"""Given a jwe, extracts the payload and returns it in string form."""
try:
key_bytes = (
encryption_key.encode("utf-8")
if isinstance(encryption_key, str)
else encryption_key
)
key = OctKey.import_key(key_bytes)
result = jwe.decrypt_compact(jwe_string, key)
return result.plaintext.decode("utf-8")
except DecodeError as e:
logger.debug("Failed to decrypt JWE: {}", e)
raise e
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/fides/api/oauth/utils.py` around lines 59 - 72, The extract_payload
function currently catches JoseError and re-raises it, which leaves callers that
only catch DecodeError (e.g., extract_token_and_load_client and
extract_token_and_load_client_async) exposed to other JoseError subclasses;
change extract_payload (around OctKey.import_key and jwe.decrypt_compact usage)
to catch JoseError and raise a jose.exceptions.DecodeError (or the library's
DecodeError) from the caught exception so callers that only handle DecodeError
will receive the normalized exception; preserve the original exception as the
cause when raising to keep debugging info.

extract_payload(authorization, CONFIG.security.app_encryption_key)
)
except jose.exceptions.JWEParseError:
except DecodeError:
Copy link
Contributor

Choose a reason for hiding this comment

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

see the robot's comments about catching JoseError instead :)

from joserfc.jwk import OctKey

_JWT_ENCRYPTION_ALGORITHM = ALGORITHMS.A256GCM
_JWT_ENCRYPTION_ALGORITHM = "A256GCM"
Copy link
Contributor

Choose a reason for hiding this comment

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

u_u sad it's not a constant anymore

Comment on lines 48 to 50
JWT_ENCRYPTION_ALGORITHM = ALGORITHMS.A256GCM
JWT_ENCRYPTION_ALGORITHM = "A256GCM"

Copy link
Contributor

Choose a reason for hiding this comment

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

can we define this constant in a single module and use it from there instead of having it duplicated ?

Comment on lines +62 to +69
key_bytes = (
encryption_key.encode("utf-8")
if isinstance(encryption_key, str)
else encryption_key
)
key = OctKey.import_key(key_bytes)
result = jwe.decrypt_compact(jwe_string, key)
return result.plaintext.decode("utf-8")
Copy link
Contributor

Choose a reason for hiding this comment

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

this code is the same as decrypt_jwe right? can we call that function instead of duplicating it?

…t miss broader JOSE exceptions, consolidated the encryption string and functions
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/fides/api/oauth/utils.py (1)

135-140: ⚠️ Potential issue | 🟡 Minor

Catch ValueError on these token parsing branches too.

These try blocks wrap both extract_payload(...) and json.loads(...). JSONDecodeError and decode-related ValueErrors will currently bypass these handlers and bubble out as 500s, while other auth-token handlers in the same file already normalize both exception types.

🛡️ Suggested fix
-    except JoseError:
+    except (JoseError, ValueError):

Apply the same change at lines 135-140, 163-168, and 207-212.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/fides/api/oauth/utils.py` around lines 135 - 140, Update the try/except
blocks that call extract_payload(...) followed by json.loads(...) so they catch
decode-related errors: change except JoseError: to except (JoseError,
ValueError): and re-raise AuthorizationError(detail="Not Authorized for this
action"); apply this same change to each token-parsing block in the file where
extract_payload and json.loads are used (the three similar try/except sites) so
JSONDecodeError/ValueError no longer bubble up as 500s.
🧹 Nitpick comments (1)
src/fides/api/oauth/utils.py (1)

57-61: Use bare raise to re-raise the caught exception.

In Python, bare raise is the idiomatic way to re-raise an exception without modification. It maintains the original traceback context with clearer intent than raise e.

♻️ Suggested fix
     except JoseError as e:
         logger.debug("Failed to decrypt JWE: {}", e)
-        raise e
+        raise
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/fides/api/oauth/utils.py` around lines 57 - 61, The except block catching
JoseError in the decrypt_jwe call should re-raise the original exception using a
bare raise to preserve the original traceback; update the except clause in the
code handling decrypt_jwe to call logger.debug("Failed to decrypt JWE: {}", e)
(or preferably logger.debug with proper formatting) and replace "raise e" with a
bare "raise" so the original traceback is retained when re-raising the caught
JoseError.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Outside diff comments:
In `@src/fides/api/oauth/utils.py`:
- Around line 135-140: Update the try/except blocks that call
extract_payload(...) followed by json.loads(...) so they catch decode-related
errors: change except JoseError: to except (JoseError, ValueError): and re-raise
AuthorizationError(detail="Not Authorized for this action"); apply this same
change to each token-parsing block in the file where extract_payload and
json.loads are used (the three similar try/except sites) so
JSONDecodeError/ValueError no longer bubble up as 500s.

---

Nitpick comments:
In `@src/fides/api/oauth/utils.py`:
- Around line 57-61: The except block catching JoseError in the decrypt_jwe call
should re-raise the original exception using a bare raise to preserve the
original traceback; update the except clause in the code handling decrypt_jwe to
call logger.debug("Failed to decrypt JWE: {}", e) (or preferably logger.debug
with proper formatting) and replace "raise e" with a bare "raise" so the
original traceback is retained when re-raising the caught JoseError.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7d3c1b1e-6bc5-4169-8066-25c53f00f73c

📥 Commits

Reviewing files that changed from the base of the PR and between 6bb6fda and 9aba97c.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (3)
  • pyproject.toml
  • src/fides/api/oauth/jwt.py
  • src/fides/api/oauth/utils.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • pyproject.toml

Copy link
Contributor

@erosselli erosselli left a comment

Choose a reason for hiding this comment

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

We're still catching DecodeError in src/fides/api/api/v1/endpoints/user_endpoints.py , should that be JoseError? Don't forget the changelog entry

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/fides/api/oauth/jwt.py (1)

7-22: Implementation correctly uses the joserfc API.

The function properly:

  • Converts the encryption key to bytes
  • Imports it as an OctKey for symmetric encryption
  • Calls encrypt_compact with the correct parameters: protected header dict, plaintext bytes, and key object

Minor note: The type annotation specifies encryption_key: str, but the code includes an else branch handling bytes (lines 14–15). If the function should accept both types, update the annotation to Union[str, bytes]; otherwise, remove the unreachable else branch.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/fides/api/oauth/jwt.py` around lines 7 - 22, The type annotation for
generate_jwe claims encryption_key: str but the implementation accepts bytes
too; update the signature to accept Union[str, bytes] (import Union from typing)
so the parameter type matches the branch that handles both str and bytes,
leaving the existing conversion logic (the isinstance check and
OctKey.import_key call) intact; alternatively, if you only want str, remove the
else branch that handles bytes—reference generate_jwe and the encryption_key
parameter when making this change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/fides/api/oauth/jwt.py`:
- Around line 7-22: The type annotation for generate_jwe claims encryption_key:
str but the implementation accepts bytes too; update the signature to accept
Union[str, bytes] (import Union from typing) so the parameter type matches the
branch that handles both str and bytes, leaving the existing conversion logic
(the isinstance check and OctKey.import_key call) intact; alternatively, if you
only want str, remove the else branch that handles bytes—reference generate_jwe
and the encryption_key parameter when making this change.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 7107ed3a-be00-4876-902b-ea60a863b82b

📥 Commits

Reviewing files that changed from the base of the PR and between 9aba97c and 60d6724.

📒 Files selected for processing (3)
  • changelog/7573.yaml
  • src/fides/api/api/v1/endpoints/user_endpoints.py
  • src/fides/api/oauth/jwt.py
✅ Files skipped from review due to trivial changes (1)
  • changelog/7573.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/fides/api/api/v1/endpoints/user_endpoints.py

@johnewart johnewart added this pull request to the merge queue Mar 7, 2026
Merged via the queue into main with commit d1958d1 Mar 7, 2026
58 checks passed
@johnewart johnewart deleted the johnewart/ENG-2691 branch March 7, 2026 01:11
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