Skip to content

Commit e942022

Browse files
committed
fix(jwk): missing auth header correctly returns 401 error
1 parent 55d38af commit e942022

3 files changed

Lines changed: 29 additions & 22 deletions

File tree

src/authentication/jwk_token.py

Lines changed: 13 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
from cachetools import TTLCache
1717
from fastapi import HTTPException, Request
1818

19-
from authentication.interface import NO_AUTH_TUPLE, AuthInterface, AuthTuple
19+
from authentication.interface import AuthInterface, AuthTuple
2020
from authentication.utils import extract_user_token
2121
from constants import (
2222
DEFAULT_VIRTUAL_PATH,
@@ -163,24 +163,27 @@ def __init__(
163163
async def __call__(self, request: Request) -> AuthTuple:
164164
"""Authenticate the JWT in the headers against the keys from the JWK url.
165165
166-
If the Authorization header is missing, returns NO_AUTH_TUPLE. On token
167-
verification or validation failures this function raises HTTPException
168-
with appropriate HTTP status codes:
169-
- 401 for unknown signing key/algorithm, bad signature, expired token,
170-
or missing required claims;
166+
When the Authorization header is missing, this method raises
167+
HTTPException with status 401 (Unauthorized). On token verification or
168+
validation failures it also raises HTTPException with appropriate
169+
status codes:
171170
- 400 for token decode or other JOSE-related decode/validation errors;
171+
- 401 for missing Authorization header, unknown signing key/algorithm,
172+
bad signature, expired token, or missing required claims;
172173
- 500 for unexpected internal errors.
173174
174175
Parameters:
175-
request (Request): The incoming FastAPI request containing the Authorization header.
176+
request (Request): The incoming FastAPI request; must include the
177+
Authorization header (Bearer token) or 401 is raised.
176178
177179
Returns:
178180
AuthTuple: A tuple (user_id, username, skip_userid_check, token)
179-
extracted from the validated token, or NO_AUTH_TUPLE when no
180-
Authorization header is present.
181+
extracted from the validated JWT. Only returned on successful
182+
authentication; all error paths raise HTTPException.
181183
"""
182184
if not request.headers.get("Authorization"):
183-
return NO_AUTH_TUPLE
185+
response = UnauthorizedResponse(cause="No Authorization header found")
186+
raise HTTPException(**response.model_dump())
184187

185188
user_token = extract_user_token(request.headers)
186189

tests/e2e/features/rbac.feature

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,14 +12,20 @@ Feature: Role-Based Access Control (RBAC)
1212
# Authentication - Token Validation
1313
# ============================================
1414

15-
#https://issues.redhat.com/browse/LCORE-1210
16-
@skip
1715
Scenario: Request without token returns 401
1816
Given The system is in default state
1917
And I remove the auth header
2018
When I access REST API endpoint "models" using HTTP GET method
2119
Then The status code of the response is 401
22-
And The body of the response contains Missing or invalid credentials
20+
And The body of the response is the following
21+
"""
22+
{
23+
"detail": {
24+
"response": "Missing or invalid credentials provided by client",
25+
"cause": "No Authorization header found"
26+
}
27+
}
28+
"""
2329

2430
Scenario: Request with malformed Authorization header returns 401
2531
Given The system is in default state

tests/unit/authentication/test_jwk_token.py

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
from authlib.jose import JsonWebKey, JsonWebToken
1414

1515
from authentication.jwk_token import JwkTokenAuthDependency, _jwk_cache
16-
from constants import DEFAULT_USER_NAME, DEFAULT_USER_UID, NO_USER_TOKEN
1716
from models.config import JwkConfiguration, JwtConfiguration
1817

1918
TEST_USER_ID = "test-user-123"
@@ -435,19 +434,18 @@ async def test_no_auth_header(
435434
mocked_signing_keys_server: Any,
436435
no_token_request: Request,
437436
) -> None:
438-
"""Test with no Authorization header."""
437+
"""Test with no Authorization header returns 401 Unauthorized."""
439438
_ = mocked_signing_keys_server
440439

441440
dependency = JwkTokenAuthDependency(default_jwk_configuration)
442441

443-
user_id, username, skip_userid_check, token_claims = await dependency(
444-
no_token_request
445-
)
442+
with pytest.raises(HTTPException) as exc_info:
443+
await dependency(no_token_request)
446444

447-
assert user_id == DEFAULT_USER_UID
448-
assert username == DEFAULT_USER_NAME
449-
assert skip_userid_check is True
450-
assert token_claims == NO_USER_TOKEN
445+
assert exc_info.value.status_code == 401
446+
detail = cast(dict, exc_info.value.detail)
447+
assert detail["cause"] == "No Authorization header found"
448+
assert detail["response"] == "Missing or invalid credentials provided by client"
451449

452450

453451
async def test_no_bearer(

0 commit comments

Comments
 (0)