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

22 statements  

« prev     ^ index     » next       coverage.py v7.8.0, created at 2025-05-21 20:31 +0100

1from __future__ import annotations 

2 

3import inspect 

4from typing_extensions import Annotated, Doc 

5 

6from .base import AuthenticationBackend, UnauthenticatedUser, BaseUser 

7from nexios.middlewares.base import BaseMiddleware 

8from nexios.http import Request, Response 

9import typing 

10 

11 

12class 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()