Coverage for nexios\auth\backends\apikey.py: 41%

1 from typing import Any, Callable, Awaitable

2 from typing_extensions import Annotated, Doc

3 from nexios.auth.base import AuthenticationBackend, UnauthenticatedUser

4 from nexios.http import Request, Response

5

6

7 class APIKeyAuthBackend(AuthenticationBackend):

8 """

9 Authentication backend for API key-based authentication.

10

11 This class verifies incoming requests using API keys found in request headers.

12 It relies on a user-defined authentication function to validate API keys.

13

14 Attributes:

15 authenticate_func (Callable[..., Awaitable[Any]]): The function used to validate API keys.

16 header_name (str): The HTTP header used to pass the API key (default: "X-API-Key").

17 """

18

19 def __init__(

20 self,

21 authenticate_func: Annotated[

22 Callable[..., Awaitable[Any]],

23 Doc(

24 "Function that takes an API key and returns a user object if valid, None otherwise."

25 ),

26 ],

27 header_name: Annotated[

28 str,

29 Doc(

30 'The header name from which the API key is retrieved (default: "X-API-Key").'

31 ),

32 ] = "X-API-Key",

33 ) -> None:

34 """

35 Initializes the APIKeyAuthBackend with an authentication function and optional header name.

36

37 Args:

38 authenticate_func (Callable[..., Awaitable[Any]]): Function to validate API keys.

39 header_name (str, optional): Header key where the API key is expected (default: "X-API-Key").

40 """

41 self.authenticate_func = authenticate_func

42 self.header_name = header_name

43

44 async def authenticate( # type: ignore[override]

45 self,

46 request: Annotated[

47 Request,

48 Doc(

49 "The incoming HTTP request, containing authentication credentials in headers."

50 ),

51 ],

52 response: Annotated[

53 Response,

54 Doc(

55 "The HTTP response object, which may be modified for authentication-related headers."

56 ),

57 ],

58 ) -> Any:

59 """

60 Authenticates the request by checking for an API key in the specified header.

61

62 This method extracts the API key from the request headers and verifies it

63 using the provided authentication function.

64

65 Args:

66 request (Request): The incoming HTTP request.

67 response (Response): The response object (may be modified).

68

69 Returns:

70 Any: A user object if authentication is successful, `UnauthenticatedUser` if invalid, or `None` if no API key is provided.

71

72 Side Effects:

73 - If no API key is found, sets the `WWW-Authenticate` response header.

74 """

75 # Retrieve the API key from the request headers

76 api_key = request.headers.get(self.header_name)

77 if not api_key:

78 response.set_header("WWW-Authenticate", 'APIKey realm="Access to the API"')

79 return None

80

81 # Authenticate the API key using the provided function

82 user = await self.authenticate_func(api_key)

83 if not user:

84 return UnauthenticatedUser()

85

86 return user, "apikey"