Coverage for nexios\auth\decorator.py: 83%
29 statements
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-21 20:31 +0100
« prev ^ index » next coverage.py v7.8.0, created at 2025-05-21 20:31 +0100
1from nexios.decorators import RouteDecorator
2import typing
3from .exceptions import AuthenticationFailed
4from nexios.http import Request, Response
5from functools import wraps
8class auth(RouteDecorator):
10 def __init__(self, scopes: typing.Union[str, typing.List[str], None] = None):
11 super().__init__()
12 if isinstance(scopes, str):
13 self.scopes = [scopes]
14 elif scopes is None:
15 self.scopes = [] # Allow authentication with any scope
16 else:
17 self.scopes = scopes
19 def __call__(
20 self, handler: typing.Callable[..., typing.Awaitable[typing.Any]]
21 ) -> typing.Any:
23 if getattr(handler, "_is_wrapped", False):
24 return handler
26 @wraps(handler) # type: ignore
27 async def wrapper(
28 *args: typing.List[typing.Any], **kwargs: typing.Dict[str, typing.Any]
29 ) -> typing.Any:
30 request, response, *_ = kwargs.values()
32 if not isinstance(request, Request) or not isinstance(response, Response):
33 raise TypeError("Expected request and response as the fist arguments")
35 if not request.scope.get("user"):
36 raise AuthenticationFailed
38 scope = request.scope.get("auth") # type: ignore
40 if self.scopes and scope not in self.scopes:
41 raise AuthenticationFailed
43 return await handler(*args, **kwargs)
45 wrapper._is_wrapped = True # type: ignore
46 return wrapper