fix: redirect to login on authorization errors for HTML pages
When a session times out or user accesses pages with wrong role, redirect to login instead of showing error page. Changes: - Extend exception handler to redirect on 403 errors with auth codes - Add tests for HTML page auth redirect behavior Error codes that trigger redirect: - ADMIN_REQUIRED, INSUFFICIENT_PERMISSIONS, USER_NOT_ACTIVE - VENDOR_ACCESS_DENIED, UNAUTHORIZED_VENDOR_ACCESS - VENDOR_OWNER_ONLY, INSUFFICIENT_VENDOR_PERMISSIONS - CUSTOMER_NOT_AUTHORIZED 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -31,12 +31,41 @@ def setup_exception_handlers(app):
|
||||
async def custom_exception_handler(request: Request, exc: WizamartException):
|
||||
"""Handle custom exceptions with context-aware rendering."""
|
||||
|
||||
# Special handling for 401 on HTML page requests (redirect to login)
|
||||
if exc.status_code == 401 and _is_html_page_request(request):
|
||||
# Special handling for auth errors on HTML page requests (redirect to login)
|
||||
# This includes both:
|
||||
# - 401 errors: Not authenticated (expired/invalid token)
|
||||
# - 403 errors with specific auth codes: Authenticated but wrong context
|
||||
# (e.g., vendor token on admin page, role mismatch)
|
||||
# These codes indicate the user should re-authenticate with correct credentials
|
||||
auth_redirect_error_codes = {
|
||||
# Auth-level errors
|
||||
"ADMIN_REQUIRED",
|
||||
"INSUFFICIENT_PERMISSIONS",
|
||||
"USER_NOT_ACTIVE",
|
||||
# Vendor-level auth errors
|
||||
"VENDOR_ACCESS_DENIED",
|
||||
"UNAUTHORIZED_VENDOR_ACCESS",
|
||||
"VENDOR_OWNER_ONLY",
|
||||
"INSUFFICIENT_VENDOR_PERMISSIONS",
|
||||
# Customer-level auth errors
|
||||
"CUSTOMER_NOT_AUTHORIZED",
|
||||
}
|
||||
|
||||
should_redirect = (
|
||||
_is_html_page_request(request)
|
||||
and (
|
||||
exc.status_code == 401
|
||||
or (exc.status_code == 403 and exc.error_code in auth_redirect_error_codes)
|
||||
)
|
||||
)
|
||||
|
||||
if should_redirect:
|
||||
logger.info(
|
||||
f"401 on HTML page request - redirecting to login: {request.url.path}",
|
||||
f"Auth error on HTML page request - redirecting to login: {request.url.path}",
|
||||
extra={
|
||||
"path": request.url.path,
|
||||
"status_code": exc.status_code,
|
||||
"error_code": exc.error_code,
|
||||
"accept": request.headers.get("accept", ""),
|
||||
"method": request.method,
|
||||
},
|
||||
|
||||
@@ -48,3 +48,65 @@ class TestAuthorization:
|
||||
)
|
||||
# Admin should be able to view vendor
|
||||
assert response.status_code == 200
|
||||
|
||||
|
||||
@pytest.mark.integration
|
||||
@pytest.mark.security
|
||||
@pytest.mark.auth
|
||||
class TestHTMLPageAuthRedirect:
|
||||
"""
|
||||
Test that authorization errors on HTML pages redirect to login.
|
||||
|
||||
For HTML page requests (Accept: text/html), both 401 and specific 403 errors
|
||||
should redirect to the appropriate login page instead of showing error pages.
|
||||
"""
|
||||
|
||||
def test_api_request_returns_json_on_auth_error(self, client, auth_headers):
|
||||
"""Test that API requests return JSON error, not redirect."""
|
||||
# Non-admin user trying to access admin API endpoint
|
||||
response = client.get("/api/v1/admin/users", headers=auth_headers)
|
||||
assert response.status_code == 403
|
||||
# Should be JSON, not redirect
|
||||
data = response.json()
|
||||
assert data["error_code"] == "ADMIN_REQUIRED"
|
||||
|
||||
def test_html_page_redirects_on_no_token(self, client):
|
||||
"""Test that HTML page requests without token redirect to login."""
|
||||
# Request admin page without token, accepting HTML
|
||||
response = client.get(
|
||||
"/admin/dashboard",
|
||||
headers={"Accept": "text/html"},
|
||||
follow_redirects=False,
|
||||
)
|
||||
# Should redirect (302) to login page
|
||||
assert response.status_code == 302
|
||||
assert "/admin/login" in response.headers.get("location", "")
|
||||
|
||||
def test_html_page_redirects_on_invalid_token(self, client):
|
||||
"""Test that HTML page requests with invalid token redirect to login."""
|
||||
# Request admin page with invalid token cookie, accepting HTML
|
||||
response = client.get(
|
||||
"/admin/dashboard",
|
||||
headers={"Accept": "text/html"},
|
||||
cookies={"admin_token": "invalid.token.here"},
|
||||
follow_redirects=False,
|
||||
)
|
||||
# Should redirect (302) to login page
|
||||
assert response.status_code == 302
|
||||
assert "/admin/login" in response.headers.get("location", "")
|
||||
|
||||
def test_html_page_redirects_on_admin_required(self, client, auth_headers):
|
||||
"""Test that HTML page requests with wrong role redirect to login."""
|
||||
# Regular user (not admin) trying to access admin HTML page
|
||||
# We need to set both the cookie and Accept header for HTML behavior
|
||||
response = client.get(
|
||||
"/admin/dashboard",
|
||||
headers={
|
||||
"Accept": "text/html",
|
||||
"Authorization": auth_headers["Authorization"],
|
||||
},
|
||||
follow_redirects=False,
|
||||
)
|
||||
# Should redirect (302) to login page when user lacks admin role
|
||||
assert response.status_code == 302
|
||||
assert "/admin/login" in response.headers.get("location", "")
|
||||
|
||||
Reference in New Issue
Block a user