Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 8 additions & 3 deletions agentflow_cli/src/app/core/auth/auth_backend.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
from typing import Any

from fastapi import Depends, Response
from fastapi import Depends, Request, Response
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
from injectq.integrations import InjectAPI

Expand All @@ -10,7 +10,8 @@


def verify_current_user(
res: Response,
request: Request,
response: Response,
credential: HTTPAuthorizationCredentials = Depends(
HTTPBearer(auto_error=False),
),
Expand All @@ -27,7 +28,11 @@ def verify_current_user(
logger.error("Auth backend is not configured")
return user

user: dict | None = auth_backend.authenticate(res, credential)
user: dict | None = auth_backend.authenticate(
request,
response,
credential,
)
if user and "user_id" not in user:
logger.error("Authentication failed: 'user_id' not found in user info")
return user or {}
7 changes: 5 additions & 2 deletions agentflow_cli/src/app/core/auth/base_auth.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
from abc import ABC, abstractmethod
from typing import Any

from fastapi import Response
from fastapi import Request, Response
from fastapi.security import HTTPAuthorizationCredentials


class BaseAuth(ABC):
@abstractmethod
def authenticate(
self, res: Response, credential: HTTPAuthorizationCredentials
self,
request: Request,
response: Response,
credential: HTTPAuthorizationCredentials,
) -> dict[str, Any] | None:
"""Authenticate the user based on the provided credentials.
IT should return an empty dict if no authentication is required.
Expand Down
10 changes: 7 additions & 3 deletions agentflow_cli/src/app/core/auth/jwt_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
from typing import Any

import jwt
from fastapi import Response
from fastapi import Request, Response
from fastapi.security import HTTPAuthorizationCredentials

from agentflow_cli.src.app.core import logger
Expand All @@ -12,7 +12,10 @@

class JwtAuth(BaseAuth):
def authenticate(
self, res: Response, credential: HTTPAuthorizationCredentials
self,
request: Request,
response: Response,
credential: HTTPAuthorizationCredentials,
) -> dict[str, Any] | None:
"""No authentication is required, so return None."""
"""
Expand Down Expand Up @@ -72,7 +75,8 @@ def authenticate(
message="Invalid token, please login again",
error_code="INVALID_TOKEN",
)
res.headers["WWW-Authenticate"] = 'Bearer realm="auth_required"'

response.headers["WWW-Authenticate"] = 'Bearer realm="auth_required"'

# check if user_id exists in the token
if "user_id" not in decoded_token:
Expand Down
39 changes: 25 additions & 14 deletions tests/unit_tests/auth/test_auth_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
from unittest.mock import MagicMock, patch

import pytest
from fastapi import Response
from fastapi import Request, Response
from fastapi.security import HTTPAuthorizationCredentials

from agentflow_cli.src.app.core.auth.auth_backend import verify_current_user
Expand All @@ -25,7 +25,7 @@ def __init__(self, return_value: dict[str, Any] | None = None, raise_exception:
self._raise_exception = raise_exception

def authenticate(
self, res: Response, credential: HTTPAuthorizationCredentials
self, request: Request | None, res: Response, credential: HTTPAuthorizationCredentials
) -> dict[str, Any] | None:
if self._raise_exception:
raise ValueError("Authentication failed")
Expand Down Expand Up @@ -69,7 +69,8 @@ def test_returns_empty_dict_when_no_auth_backend_configured(
mock_auth_backend = MockBaseAuth(return_value={"user_id": "123"})

result = verify_current_user(
res=mock_response,
request=None,
response=mock_response,
credential=mock_credentials,
config=mock_graph_config_no_auth,
auth_backend=mock_auth_backend,
Expand All @@ -86,7 +87,8 @@ def test_returns_empty_dict_when_auth_backend_is_none(
"""Test that empty dict is returned when auth_backend is None."""
with patch("agentflow_cli.src.app.core.auth.auth_backend.logger") as mock_logger:
result = verify_current_user(
res=mock_response,
request=None,
response=mock_response,
credential=mock_credentials,
config=mock_graph_config_jwt_auth,
auth_backend=None,
Expand All @@ -110,7 +112,8 @@ def test_returns_user_dict_on_successful_authentication(
mock_auth_backend = MockBaseAuth(return_value=expected_user)

result = verify_current_user(
res=mock_response,
request=None,
response=mock_response,
credential=mock_credentials,
config=mock_graph_config_jwt_auth,
auth_backend=mock_auth_backend,
Expand All @@ -129,7 +132,8 @@ def test_returns_empty_dict_when_authenticate_returns_none(
mock_auth_backend = MockBaseAuth(return_value=None)

result = verify_current_user(
res=mock_response,
request=None,
response=mock_response,
credential=mock_credentials,
config=mock_graph_config_jwt_auth,
auth_backend=mock_auth_backend,
Expand All @@ -149,7 +153,8 @@ def test_logs_error_when_user_dict_missing_user_id(

with patch("agentflow_cli.src.app.core.auth.auth_backend.logger") as mock_logger:
result = verify_current_user(
res=mock_response,
request=None,
response=mock_response,
credential=mock_credentials,
config=mock_graph_config_jwt_auth,
auth_backend=mock_auth_backend,
Expand All @@ -173,7 +178,8 @@ def test_does_not_log_error_when_user_dict_has_user_id(

with patch("agentflow_cli.src.app.core.auth.auth_backend.logger") as mock_logger:
result = verify_current_user(
res=mock_response,
request=None,
response=mock_response,
credential=mock_credentials,
config=mock_graph_config_jwt_auth,
auth_backend=mock_auth_backend,
Expand All @@ -193,7 +199,8 @@ def test_does_not_log_error_when_authenticate_returns_empty_dict(

with patch("agentflow_cli.src.app.core.auth.auth_backend.logger") as mock_logger:
result = verify_current_user(
res=mock_response,
request=None,
response=mock_response,
credential=mock_credentials,
config=mock_graph_config_jwt_auth,
auth_backend=mock_auth_backend,
Expand All @@ -216,7 +223,8 @@ def test_returns_user_with_numeric_user_id(

with patch("agentflow_cli.src.app.core.auth.auth_backend.logger") as mock_logger:
result = verify_current_user(
res=mock_response,
request=None,
response=mock_response,
credential=mock_credentials,
config=mock_graph_config_jwt_auth,
auth_backend=mock_auth_backend,
Expand All @@ -238,7 +246,8 @@ def test_returns_user_with_empty_string_user_id(

with patch("agentflow_cli.src.app.core.auth.auth_backend.logger") as mock_logger:
result = verify_current_user(
res=mock_response,
request=None,
response=mock_response,
credential=mock_credentials,
config=mock_graph_config_jwt_auth,
auth_backend=mock_auth_backend,
Expand All @@ -260,7 +269,8 @@ def test_returns_user_with_none_user_id(

with patch("agentflow_cli.src.app.core.auth.auth_backend.logger") as mock_logger:
result = verify_current_user(
res=mock_response,
request=None,
response=mock_response,
credential=mock_credentials,
config=mock_graph_config_jwt_auth,
auth_backend=mock_auth_backend,
Expand Down Expand Up @@ -297,11 +307,12 @@ def test_passes_null_credentials_to_auth_backend(
mock_auth.authenticate.return_value = {"user_id": "123"}

verify_current_user(
res=mock_response,
request=None,
response=mock_response,
credential=None, # Null credentials
config=mock_graph_config_jwt_auth,
auth_backend=mock_auth,
)

# Verify authenticate was called with None credentials
mock_auth.authenticate.assert_called_once_with(mock_response, None)
mock_auth.authenticate.assert_called_once_with(None, mock_response, None)
Loading