Coverage for kwai/modules/identity/authenticate_user.py: 97%
31 statements
« prev ^ index » next coverage.py v7.3.0, created at 2023-09-05 17:55 +0000
« prev ^ index » next coverage.py v7.3.0, created at 2023-09-05 17:55 +0000
1"""Module that implements the use case: authenticate user."""
2from dataclasses import dataclass
3from datetime import datetime, timedelta
5from kwai.core.domain.value_objects.email_address import EmailAddress
6from kwai.modules.identity.exceptions import AuthenticationException
7from kwai.modules.identity.tokens.access_token import AccessTokenEntity
8from kwai.modules.identity.tokens.access_token_repository import AccessTokenRepository
9from kwai.modules.identity.tokens.refresh_token import RefreshTokenEntity
10from kwai.modules.identity.tokens.refresh_token_repository import RefreshTokenRepository
11from kwai.modules.identity.tokens.token_identifier import TokenIdentifier
12from kwai.modules.identity.users.user_account_repository import UserAccountRepository
15@dataclass(kw_only=True, frozen=True)
16class AuthenticateUserCommand:
17 """Input for the (AuthenticateUser) use case.
19 Attributes:
20 username: The email address of the user.
21 password: The password of the user.
22 access_token_expiry_minutes: Minutes before expiring the access token.
23 Default is 2 hours.
24 refresh_token_expiry_minutes: Minutes before expiring the refresh token.
25 Default is 2 months.
26 """
28 username: str
29 password: str
30 access_token_expiry_minutes: int = 60 * 2 # 2 hours
31 refresh_token_expiry_minutes: int = 60 * 24 * 60 # 2 months
34class AuthenticateUser:
35 """Use case to authenticate a user.
37 Attributes:
38 _user_account_repo (UserAccountRepository): The repository for getting the
39 user account.
40 _access_token_repo (UserAccountRepository): The repository for creating the
41 access token.
42 _refresh_token_repo (UserAccountRepository): The repository for creating the
43 refresh token.
44 """
46 def __init__(
47 self,
48 user_account_repo: UserAccountRepository,
49 access_token_repo: AccessTokenRepository,
50 refresh_token_repo: RefreshTokenRepository,
51 ):
52 self._user_account_repo = user_account_repo
53 self._access_token_repo = access_token_repo
54 self._refresh_token_repo = refresh_token_repo
56 async def execute(self, command: AuthenticateUserCommand) -> RefreshTokenEntity:
57 """Execute the use case.
59 Args:
60 command: The input for this use case.
62 Returns:
63 RefreshTokenEntity: On success, a refresh token entity will be returned.
65 Raises:
66 InvalidEmailException: Raised when the username
67 contains an invalid email address.
68 UserAccountNotFoundException: Raised when the user with the given email
69 address doesn't exist.
70 """
71 user_account = await self._user_account_repo.get_user_by_email(
72 EmailAddress(command.username)
73 )
74 if user_account.revoked:
75 raise AuthenticationException("User account is revoked")
77 if not user_account.login(command.password):
78 await self._user_account_repo.update(
79 user_account
80 ) # save the last unsuccessful login
81 raise AuthenticationException("Invalid password")
83 await self._user_account_repo.update(
84 user_account
85 ) # save the last successful login
87 access_token = await self._access_token_repo.create(
88 AccessTokenEntity(
89 identifier=TokenIdentifier.generate(),
90 expiration=datetime.utcnow()
91 + timedelta(minutes=command.access_token_expiry_minutes),
92 user_account=user_account,
93 )
94 )
96 return await self._refresh_token_repo.create(
97 RefreshTokenEntity(
98 identifier=TokenIdentifier.generate(),
99 expiration=datetime.utcnow()
100 + timedelta(minutes=command.refresh_token_expiry_minutes),
101 access_token=access_token,
102 )
103 )