-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
11 changed files
with
556 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
"""Authentication & Authorization Entry Points | ||
""" | ||
from fastapi import APIRouter, HTTPException | ||
from starlette.status import HTTP_200_OK, HTTP_401_UNAUTHORIZED | ||
|
||
from auth.app.dependencies import AuthDependency | ||
from auth.domain.commands import AuthenticateUser, AuthorizeToken | ||
from auth.domain.events import TokenGenerated, UserAuthenticated | ||
from auth.domain.schemas import ResponseModel | ||
from auth.service_layer.errors import InvalidCredentialsError | ||
|
||
router = APIRouter(prefix='/auth', tags=['Auth']) | ||
|
||
|
||
@router.post('/token', status_code=HTTP_200_OK, tags=["Commands"]) | ||
async def log_in(command: AuthenticateUser, auth_service: AuthDependency) -> ResponseModel[TokenGenerated]: | ||
""" | ||
Authenticates a user, providing an Access Token. | ||
""" | ||
try: | ||
token = auth_service.authenticate(username=command.username, password=command.password) | ||
except InvalidCredentialsError as e: | ||
raise HTTPException(status_code=HTTP_401_UNAUTHORIZED, detail=str(e)) | ||
|
||
return ResponseModel(data=TokenGenerated(token=token)) | ||
|
||
|
||
@router.post('/user', status_code=HTTP_200_OK, tags=["Commands"]) | ||
async def authorize_token( | ||
command: AuthorizeToken, | ||
auth_service: AuthDependency) -> ResponseModel[UserAuthenticated]: | ||
""" | ||
Authorizes a token, providing the user's information. | ||
""" | ||
try: | ||
user = auth_service.authorize(command.token) | ||
except InvalidCredentialsError as e: | ||
raise HTTPException(status_code=HTTP_401_UNAUTHORIZED, detail=str(e)) | ||
|
||
return ResponseModel(data=user) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
"""Auth Service | ||
Authenticates and authorizes users. | ||
""" | ||
from auth.adapters.repository import Repository | ||
from auth.domain.events import UserAuthenticated | ||
from auth.service_layer.errors import InvalidCredentialsError | ||
from auth.service_layer.jwt import JwtService | ||
from auth.service_layer.password_encoder import PasswordEncoder | ||
|
||
|
||
class AuthService: | ||
""" | ||
Authenticates and authorizes users. | ||
""" | ||
|
||
def __init__(self, user_repository: Repository, | ||
encoder: PasswordEncoder, | ||
jwt_service: JwtService): | ||
self.user_repository = user_repository | ||
self.encoder = encoder | ||
self.jwt_service = jwt_service | ||
|
||
def _verify_credentials(self, username: str, password: str | None = None) -> UserAuthenticated: | ||
""" | ||
Verifies if the user exists. | ||
Args: | ||
username: An unique username of the user. | ||
password: The password to verify. | ||
Returns: | ||
The user authenticated. | ||
Raises: | ||
InvalidCredentialsError: If the user does not exist or the password is invalid. | ||
""" | ||
user = self.user_repository.find_by(username=username) | ||
|
||
if not user: | ||
raise InvalidCredentialsError(f"User {username} not found.") | ||
|
||
if password: | ||
self.encoder.verify(password, user.password) | ||
|
||
return UserAuthenticated(**user.dict()) | ||
|
||
def authenticate(self, username: str, password: str) -> str: | ||
""" | ||
Authenticates a user. | ||
Args: | ||
username: An unique username of the user. | ||
password: The password to verify. | ||
Returns: | ||
A Json Web Token. | ||
Raises: | ||
InvalidCredentialsError: If the user does not exist or the password is invalid. | ||
""" | ||
user = self._verify_credentials(username, password) | ||
|
||
return self.jwt_service.get_token(user) | ||
|
||
def authorize(self, token: str) -> UserAuthenticated: | ||
""" | ||
Authorizes a user. | ||
Args: | ||
token: The token to verify. | ||
Raises: | ||
InvalidCredentialsError: If the token is invalid. | ||
""" | ||
user_token = self.jwt_service.decode_user_token(token) | ||
|
||
user = self._verify_credentials(user_token.username) | ||
|
||
return user |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,84 @@ | ||
"""JWT Service | ||
""" | ||
from datetime import datetime, timedelta | ||
|
||
from jose import jwt | ||
|
||
from auth.domain.events import UserAuthenticated | ||
from auth.service_layer.errors import InvalidCredentialsError | ||
|
||
|
||
class JwtService: | ||
""" | ||
JWT Service | ||
Handles the creation and decoding of JWT tokens. | ||
""" | ||
|
||
def __init__(self, | ||
secret_key: str = "25504cafb5843158167e6b423efab76f472d94f1b276eec960b9df1cb8a90569", | ||
algorithm: str = "HS256", | ||
token_expiration_minutes: int = 60): | ||
self.secret_key = secret_key | ||
self.algorithm = algorithm | ||
self.token_expiration_minutes = token_expiration_minutes | ||
|
||
def get_token(self, event: UserAuthenticated) -> str: | ||
""" | ||
Generates a JWT token for the user. | ||
Args: | ||
event: Credentials of the user. | ||
Returns: | ||
str: A Json Web Token. | ||
Raises: | ||
JwtError: If the token could not be encoded. | ||
""" | ||
access_token_expires = timedelta(minutes=self.token_expiration_minutes) | ||
|
||
return self.create_token(payload=event.dict(), expires_delta=access_token_expires) | ||
|
||
def create_token(self, payload: dict, expires_delta: timedelta) -> str: | ||
""" | ||
Encodes the payload into a JWT token. | ||
Args: | ||
payload: | ||
expires_delta: | ||
Returns: | ||
str: A JWT token. | ||
Raises: | ||
JwtError: If the token could not be encoded. | ||
""" | ||
to_encode = payload.copy() | ||
expire = datetime.utcnow() + expires_delta | ||
to_encode.update({"exp": expire}) | ||
|
||
return jwt.encode(to_encode, self.secret_key, algorithm=self.algorithm) | ||
|
||
def decode_user_token(self, token: str) -> UserAuthenticated: | ||
""" | ||
Decodes a JWT token. | ||
Args: | ||
token: A JWT token. | ||
Returns: | ||
dict: The decoded token. | ||
Raises: | ||
InvalidCredentialsError: If there's an error decoding the token. | ||
""" | ||
try: | ||
payload = jwt.decode(token, self.secret_key, algorithms=[self.algorithm]) | ||
|
||
user_token = UserAuthenticated(**payload) | ||
|
||
return user_token | ||
except jwt.JWTError as e: | ||
raise InvalidCredentialsError(e) from e |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.