Skip to content

LCORE-1210: Missing JWK auth header correctly returns 401 error#1344

Merged
tisnik merged 1 commit into
lightspeed-core:mainfrom
asimurka:fix_jwk_missing_auth_header_returns_403
Mar 18, 2026
Merged

LCORE-1210: Missing JWK auth header correctly returns 401 error#1344
tisnik merged 1 commit into
lightspeed-core:mainfrom
asimurka:fix_jwk_missing_auth_header_returns_403

Conversation

@asimurka

@asimurka asimurka commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

Description

This PR fixes bug in JWK auth that previously returned 403 Forbidden error instead of 401 Unauthorized.

Type of change

  • Refactor
  • New feature
  • Bug fix
  • CVE fix
  • Optimization
  • Documentation Update
  • Configuration Update
  • Bump-up service version
  • Bump-up dependent library
  • Bump-up library or tool used for development (does not change the final image)
  • CI configuration change
  • Konflux configuration change
  • Unit tests improvement
  • Integration tests improvement
  • End to end tests improvement
  • Benchmarks improvement

Tools used to create PR

Identify any AI code assistants used in this PR (for transparency and review context)

  • Assisted-by: N/A
  • Generated by: N/A

Related Tickets & Documents

Checklist before requesting a review

  • I have performed a self-review of my code.
  • PR has passed all pre-merge test jobs.
  • If it is a core feature, I have added thorough tests.

Testing

  • Please provide detailed steps to perform tests related to this code change.
  • How were the fix/results from this change verified? Please provide relevant screenshots or results.

Summary by CodeRabbit

  • Bug Fixes
    • Improved handling when the Authorization header is missing: clients now receive a clear 401 Unauthorized response with an explicit JSON error payload ("No Authorization header found").
  • Tests
    • End-to-end tests updated to assert the explicit JSON error response for missing authorization.

@coderabbitai

coderabbitai Bot commented Mar 18, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@asimurka has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 16 minutes and 12 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: b170d750-712a-4103-9cd5-de472002551d

📥 Commits

Reviewing files that changed from the base of the PR and between d92f6db and e942022.

📒 Files selected for processing (3)
  • src/authentication/jwk_token.py
  • tests/e2e/features/rbac.feature
  • tests/unit/authentication/test_jwk_token.py

Walkthrough

Authorization handling in src/authentication/jwk_token.py was changed to raise an HTTPException (401) with an UnauthorizedResponse when the Authorization header is missing; the NO_AUTH_TUPLE import/export was removed. Unit and E2E tests were updated to expect the exception and explicit JSON error payload.

Changes

Cohort / File(s) Summary
Auth implementation
src/authentication/jwk_token.py
Removed NO_AUTH_TUPLE from imports/exports; when Authorization header is absent, raise HTTPException(status_code=401) with UnauthorizedResponse instead of returning a sentinel tuple.
Unit tests
tests/unit/authentication/test_jwk_token.py
Updated test_no_auth_header to expect a raised HTTPException (401) with detail containing "No Authorization header found" instead of asserting a returned tuple.
End-to-end tests
tests/e2e/features/rbac.feature
Removed skip annotation on first scenario and replaced generic 401 assertion with explicit JSON response body assertion detailing response and cause fields for the missing token case.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~12 minutes

🚥 Pre-merge checks | ✅ 3
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'LCORE-1210: Missing JWK auth header correctly returns 401 error' is clear, specific, and directly reflects the main change: fixing JWK authentication to return 401 instead of 403 when Authorization header is missing.
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 unit tests (beta)
  • Create PR with unit tests
📝 Coding Plan
  • Generate coding plan for human review comments

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@asimurka asimurka requested a review from radofuchs March 18, 2026 09:58
@asimurka asimurka force-pushed the fix_jwk_missing_auth_header_returns_403 branch from ca9f102 to d92f6db Compare March 18, 2026 10:01

@coderabbitai coderabbitai Bot 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.

Caution

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

⚠️ Outside diff range comments (3)
src/authentication/jwk_token.py (2)

19-19: ⚠️ Potential issue | 🟠 Major

Remove unused NO_AUTH_TUPLE import to unblock CI.

Ruff/Pylint are failing because NO_AUTH_TUPLE is no longer used after switching to exception-based handling for missing Authorization headers.

Suggested fix
-from authentication.interface import NO_AUTH_TUPLE, AuthInterface, AuthTuple
+from authentication.interface import AuthInterface, AuthTuple
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/authentication/jwk_token.py` at line 19, Remove the unused import
NO_AUTH_TUPLE from the import statement in jwk_token.py (currently importing
NO_AUTH_TUPLE, AuthInterface, AuthTuple); update the import to only import
AuthInterface and AuthTuple so the module no longer imports an unused symbol and
linter/CI errors are resolved.

166-180: ⚠️ Potential issue | 🟡 Minor

Update __call__ docstring to match the new missing-header behavior.

The docstring still says missing Authorization returns NO_AUTH_TUPLE, but Line 183-184 now raises HTTPException (401).

Suggested fix
-        If the Authorization header is missing, returns NO_AUTH_TUPLE. On token
+        If the Authorization header is missing, raises HTTPException(401). On token
         verification or validation failures this function raises HTTPException
         with appropriate HTTP status codes:
@@
-            AuthTuple: A tuple (user_id, username, skip_userid_check, token)
-            extracted from the validated token, or NO_AUTH_TUPLE when no
-            Authorization header is present.
+            AuthTuple: A tuple (user_id, username, skip_userid_check, token)
+            extracted from the validated token.
+
+        Raises:
+            HTTPException: 401 when Authorization header is missing or token is invalid.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/authentication/jwk_token.py` around lines 166 - 180, The docstring for
__call__ is out of sync: it still states that a missing Authorization header
returns NO_AUTH_TUPLE but the implementation now raises an HTTPException(401).
Update the __call__ docstring (parameters/behavior/returns) to state that when
the Authorization header is missing an HTTPException with status 401 is raised,
and update the Returns section to reflect that NO_AUTH_TUPLE is no longer
returned on missing header (or clarify when NO_AUTH_TUPLE is returned if still
applicable); reference the __call__ method and the HTTPException(401) behavior
so the documentation matches the code paths for missing header, verification
failures, and other error statuses.
tests/unit/authentication/test_jwk_token.py (1)

16-16: ⚠️ Potential issue | 🟠 Major

Remove unused constants import to fix lint failures.

DEFAULT_USER_NAME, DEFAULT_USER_UID, and NO_USER_TOKEN are no longer used and currently fail Ruff/Pylint.

Suggested fix
-from constants import DEFAULT_USER_NAME, DEFAULT_USER_UID, NO_USER_TOKEN
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/authentication/test_jwk_token.py` at line 16, The
test_jwk_token.py file imports three unused constants (DEFAULT_USER_NAME,
DEFAULT_USER_UID, NO_USER_TOKEN) which cause lint failures; remove those symbols
from the import statement so only actually used names are imported (or remove
the entire import if none are used) and run the linter to confirm; specifically
edit the import line that currently references DEFAULT_USER_NAME,
DEFAULT_USER_UID, and NO_USER_TOKEN to drop them.
🧹 Nitpick comments (1)
tests/unit/authentication/test_jwk_token.py (1)

443-447: Prefer structured detail assertions over substring matching.

Line 447 currently checks a stringified dict; asserting exact detail["cause"]/detail["response"] is more stable and clearer.

Suggested refactor
     with pytest.raises(HTTPException) as exc_info:
         await dependency(no_token_request)

     assert exc_info.value.status_code == 401
-    assert "No Authorization header found" in str(exc_info.value.detail)
+    detail = cast(dict[str, str], exc_info.value.detail)
+    assert detail["response"] == "Missing or invalid credentials provided by client"
+    assert detail["cause"] == "No Authorization header found"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/authentication/test_jwk_token.py` around lines 443 - 447, Replace
the substring assertion on the stringified HTTPException detail with structured
assertions: after raising via await dependency(no_token_request) and capturing
exc_info, assert exc_info.value.status_code == 401, then treat
exc_info.value.detail as a dict and assert exact values for the relevant keys
(e.g., detail["cause"] == "<expected cause>" and detail["response"] ==
"<expected response>") rather than using "in str(...)" so tests on dependency
and no_token_request validate concrete detail["cause"]/detail["response"]
values.
🤖 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/authentication/jwk_token.py`:
- Line 19: Remove the unused import NO_AUTH_TUPLE from the import statement in
jwk_token.py (currently importing NO_AUTH_TUPLE, AuthInterface, AuthTuple);
update the import to only import AuthInterface and AuthTuple so the module no
longer imports an unused symbol and linter/CI errors are resolved.
- Around line 166-180: The docstring for __call__ is out of sync: it still
states that a missing Authorization header returns NO_AUTH_TUPLE but the
implementation now raises an HTTPException(401). Update the __call__ docstring
(parameters/behavior/returns) to state that when the Authorization header is
missing an HTTPException with status 401 is raised, and update the Returns
section to reflect that NO_AUTH_TUPLE is no longer returned on missing header
(or clarify when NO_AUTH_TUPLE is returned if still applicable); reference the
__call__ method and the HTTPException(401) behavior so the documentation matches
the code paths for missing header, verification failures, and other error
statuses.

In `@tests/unit/authentication/test_jwk_token.py`:
- Line 16: The test_jwk_token.py file imports three unused constants
(DEFAULT_USER_NAME, DEFAULT_USER_UID, NO_USER_TOKEN) which cause lint failures;
remove those symbols from the import statement so only actually used names are
imported (or remove the entire import if none are used) and run the linter to
confirm; specifically edit the import line that currently references
DEFAULT_USER_NAME, DEFAULT_USER_UID, and NO_USER_TOKEN to drop them.

---

Nitpick comments:
In `@tests/unit/authentication/test_jwk_token.py`:
- Around line 443-447: Replace the substring assertion on the stringified
HTTPException detail with structured assertions: after raising via await
dependency(no_token_request) and capturing exc_info, assert
exc_info.value.status_code == 401, then treat exc_info.value.detail as a dict
and assert exact values for the relevant keys (e.g., detail["cause"] ==
"<expected cause>" and detail["response"] == "<expected response>") rather than
using "in str(...)" so tests on dependency and no_token_request validate
concrete detail["cause"]/detail["response"] values.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: ed7f9638-a3e8-4cd6-b8cf-7405607a2531

📥 Commits

Reviewing files that changed from the base of the PR and between 55d38af and ca9f102.

📒 Files selected for processing (3)
  • src/authentication/jwk_token.py
  • tests/e2e/features/rbac.feature
  • tests/unit/authentication/test_jwk_token.py

@asimurka asimurka force-pushed the fix_jwk_missing_auth_header_returns_403 branch from d92f6db to 659c6e9 Compare March 18, 2026 10:09

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
tests/unit/authentication/test_jwk_token.py (1)

437-446: Assert the structured 401 payload explicitly, not via string matching.

str(exc_info.value.detail) is a loose check. This test should validate the response contract fields directly (response and cause) to prevent silent regressions.

✅ Suggested test assertion tightening
     with pytest.raises(HTTPException) as exc_info:
         await dependency(no_token_request)

     assert exc_info.value.status_code == 401
-    assert "No Authorization header found" in str(exc_info.value.detail)
+    detail = cast(dict[str, str], exc_info.value.detail)
+    assert detail["response"] == "Missing or invalid credentials provided by client"
+    assert detail["cause"] == "No Authorization header found"
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/unit/authentication/test_jwk_token.py` around lines 437 - 446, Replace
the loose string check on str(exc_info.value.detail) with explicit assertions
against the structured error payload returned by JwkTokenAuthDependency: after
asserting status_code == 401, assert exc_info.value.detail is a dict (or has
attributes) and then check exc_info.value.detail["response"] equals the expected
message (e.g., "No Authorization header found") and
exc_info.value.detail["cause"] equals the expected cause (or is None/empty as
appropriate); reference the JwkTokenAuthDependency, default_jwk_configuration,
and no_token_request variables to locate the failing case and tighten the
assertions on the response contract rather than using string matching.
🤖 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/authentication/jwk_token.py`:
- Around line 183-184: The __call__ method's docstring in jwk_token.py is
outdated: it claims the function returns NO_AUTH_TUPLE but the code now raises
HTTPException via UnauthorizedResponse when the Authorization header is missing;
update the __call__ docstring to describe the new behavior (raise HTTPException
with UnauthorizedResponse details on missing/invalid Authorization header) and
mention any returned types when successful, referencing the __call__ method name
and the UnauthorizedResponse/HTTPException behavior so the docstring matches the
implementation.

---

Nitpick comments:
In `@tests/unit/authentication/test_jwk_token.py`:
- Around line 437-446: Replace the loose string check on
str(exc_info.value.detail) with explicit assertions against the structured error
payload returned by JwkTokenAuthDependency: after asserting status_code == 401,
assert exc_info.value.detail is a dict (or has attributes) and then check
exc_info.value.detail["response"] equals the expected message (e.g., "No
Authorization header found") and exc_info.value.detail["cause"] equals the
expected cause (or is None/empty as appropriate); reference the
JwkTokenAuthDependency, default_jwk_configuration, and no_token_request
variables to locate the failing case and tighten the assertions on the response
contract rather than using string matching.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: d632282f-a0dc-4661-9142-a84900c602d3

📥 Commits

Reviewing files that changed from the base of the PR and between ca9f102 and d92f6db.

📒 Files selected for processing (3)
  • src/authentication/jwk_token.py
  • tests/e2e/features/rbac.feature
  • tests/unit/authentication/test_jwk_token.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tests/e2e/features/rbac.feature

Comment thread src/authentication/jwk_token.py
@asimurka asimurka force-pushed the fix_jwk_missing_auth_header_returns_403 branch from 659c6e9 to e942022 Compare March 18, 2026 10:10
@asimurka asimurka requested a review from tisnik March 18, 2026 10:14

@radofuchs radofuchs 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.

LGTM

@tisnik tisnik 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.

LGTM

@tisnik tisnik merged commit 6052036 into lightspeed-core:main Mar 18, 2026
24 checks passed
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.

3 participants