Coverage for nexios\auth\middleware.py: 95%

1 from __future__ import annotations

2

3 import inspect

4 from typing_extensions import Annotated, Doc

5

6 from .base import AuthenticationBackend, UnauthenticatedUser, BaseUser

7 from nexios.middlewares.base import BaseMiddleware

8 from nexios.http import Request, Response

9 import typing

10

11

12 class AuthenticationMiddleware(BaseMiddleware):

13 """

14 Middleware responsible for handling user authentication.

15

16 This middleware intercepts incoming HTTP requests, calls the authentication

17 backend to verify user credentials, and attaches the authenticated user to

18 the request scope.

19

20 Attributes:

21 backend (AuthenticationBackend): The authentication backend used to verify users.

22 """

23

24 def __init__(

25 self,

26 backend: Annotated[

27 AuthenticationBackend,

28 Doc("The authentication backend responsible for verifying users."),

29 ],

30 ) -> None:

31 """

32 Initializes the authentication middleware with a specified backend.

33

34 Args:

35 backend (AuthenticationBackend): An instance of the authentication backend.

36 """

37 self.backend = backend

38

39 async def process_request(

40 self,

41 request: Annotated[

42 Request,

43 Doc("The HTTP request object, containing authentication credentials."),

44 ],

45 response: Annotated[

46 Response,

47 Doc(

48 "The HTTP response object, which may be modified during authentication."

49 ),

50 ],

51 call_next: typing.Callable[..., typing.Awaitable[typing.Any]],

52 ) -> None:

53 """

54 Processes an incoming request by authenticating the user.

55

56 This method calls the authentication backend, determines if the user is authenticated,

57 and attaches the authenticated user to the request. If authentication fails, the request

58 is assigned an `UnauthenticatedUser` instance.

59

60 Args:

61 request (Request): The HTTP request object.

62 response (Response): The HTTP response object.

63

64 Side Effects:

65 - Sets `request.user` to an authenticated user or `UnauthenticatedUser`.

66 - Updates `request.scope["user"]` with the user object.

67

68 """

69 if not inspect.iscoroutinefunction(self.backend.authenticate):

70 user: typing.Tuple[BaseUser, str] = self.backend.authenticate(

71 request, response

72 ) # type:ignore

73 else:

74 user: typing.Tuple[BaseUser, str] = await self.backend.authenticate(

75 request, response

76 ) # type:ignore

77

78 if user is None: # type:ignore

79 request.scope["user"] = UnauthenticatedUser()

80 request.scope["auth"] = "no-auth"

81 await call_next()

82

83 return

84

85 request.scope["user"] = user[0]

86 request.scope["auth"] = user[-1]

87

88 await call_next()