refresh tokens 0.1.2

This commit is contained in:
2026-07-23 20:51:26 +03:00
parent 7199387e6f
commit eab78b6679
8 changed files with 369 additions and 46 deletions
+153 -29
View File
@@ -1,42 +1,25 @@
from datetime import datetime, timedelta, timezone
from uuid import UUID
from fastapi import Request
from .jwt import Jwt, Hashes
from src.database.users.crud import UsersCrudActions
from src.database.auth.refresh_tokens import JwtCrudActions
from src.errors.http_errors.errors import Errors
from src.models.pydantic_models.model import UserOut
from src.models.pydantic_models.model import RefreshTokensCreate, UserOut
from src.models.configs_read.env import env_settings
class CurrentUser:
def __init__(self) -> None:
self.jwt_service=Jwt()
self.hash=Hashes()
self.crud_actions=UsersCrudActions()
self.crud_db_actions=UsersCrudActions()
self.jwt_db_actions=JwtCrudActions()
self.error=Errors()
def get_current_user(self, token:str)->UserOut:
payload=self.jwt_service.jwt_decode(token)
sub=payload.get("sub")
print(sub)
if sub is None:
raise self.error.credentials_error(detail="Jwt token is incorrect")
try:
UUID(sub)
except (ValueError, TypeError):
raise self.error.credentials_error(detail="Jwt token is incorrect")
user=self.crud_actions.get_user_by_id(UUID(sub))
if user is None:
raise self.error.not_found_error(detail="User with this email address not found")
return UserOut.model_validate(user)
def create_token(self, form_data_email:str, form_data_password:str)->dict:
user=self.crud_actions.get_user_by_email(form_data_email)
def _check(self, form_data_email:str, form_data_password:str,):
'''check user by email'''
user=self.crud_db_actions.get_user_by_email(form_data_email)
if user is None:
raise self.error.credentials_error(detail="Wrong credentials")
@@ -45,7 +28,148 @@ class CurrentUser:
if user.status is False:
raise self.error.credentials_error(detail="This user is deactivated")
return user
def get_current_user(self, token:str)->UserOut:
return {"access_token":self.jwt_service.create_token({"sub":str(user.id)}), "token_type":"bearer"}
payload=self.jwt_service.jwt_decode(token)
if (sub:=payload.get("sub")) is None:
raise self.error.credentials_error(detail="Jwt token is incorrect")
try:
sub=UUID(sub)
except (ValueError, TypeError):
raise self.error.credentials_error(detail="Jwt token is incorrect")
user=self.crud_db_actions.get_user_by_id(sub)
if user is None:
raise self.error.not_found_error(detail="User with this email address not found")
return UserOut.model_validate(user)
def create_access_token(self, form_data_email:str, form_data_password:str)->str:
'''check user info'''
user = self._check(form_data_email, form_data_password)
'''create new access token if all the checks are successful'''
return self.jwt_service.create_access_token({"sub":str(user.id)})
def create_refresh_token(self,form_data_email:str, form_data_password:str, request:Request)->str:
'''check user info'''
user=self._check(form_data_email, form_data_password)
token, jti=self.jwt_service.create_refresh_token({"sub":str(user.id)})
try:
jti=UUID(jti)
except (ValueError, TypeError):
raise self.error.credentials_error(detail="Jwt token is incorrect")
'''create new refresh token if all the checks are successful'''
token_record=RefreshTokensCreate(
id=jti,
user_id=user.id,
token_hash=self.hash.token_to_hash(token),
device_info=request.headers.get("user-agent", "unknown"),
ip_address=request.headers.get("x-forwarded-for", "").split(",")[0].strip() or (request.client.host if request.client else "unknown"),
expires_at=datetime.now(timezone.utc)+timedelta(days=env_settings.REFRESH_TOKEN_EXPIRE_DAYS)
)
self.jwt_db_actions.create_token(RefreshTokensCreate.model_dump(token_record))
return token
def refresh_token(self, refresh_token:str, expires_delta:timedelta, request:Request)->str:
'''decode old refresh token'''
old_refresh_token=self.jwt_service.jwt_decode(refresh_token)
if (sub:=old_refresh_token.get("sub")) is None or (old_jti:=old_refresh_token.get("jti")) is None:
raise self.error.credentials_error(detail="Jwt token is incorrect")
try:
old_jti=UUID(old_jti)
sub=UUID(sub)
except (ValueError, TypeError):
raise self.error.credentials_error(detail="Jwt token is incorrect")
'''old refresh token check'''
old_record=self.jwt_db_actions.get_token_by_id(old_jti)
if old_record is None:
raise self.error.not_found_error(detail="Token not found")
if old_record.is_revoked:
self.jwt_db_actions.revoke_all(old_record.user_id)
raise self.error.credentials_error(detail="Reuse token detected")
if old_record.expires_at<datetime.now(timezone.utc):
raise self.error.credentials_error(detail="Token expired")
'''user check'''
user = self.crud_db_actions.get_user_by_id(sub)
if user is None:
raise self.error.not_found_error(detail="User not found")
if user.status is False:
raise self.error.credentials_error(detail="This user is deactivated")
'''create new refresh token if all the checks are successful'''
new_refresh_token, new_jti=self.jwt_service.create_refresh_token({"sub":str(sub)})
try:
new_jti=UUID(new_jti)
except (ValueError, TypeError):
raise self.error.credentials_error(detail="Jwt token is incorrect")
'''create database record with the new token'''
new_token_record=RefreshTokensCreate(
id=new_jti,
user_id=sub,
token_hash=self.hash.token_to_hash(new_refresh_token),
device_info=request.headers.get("user-agent", "unknown"),
ip_address=request.headers.get("x-forwarded-for", "").split(",")[0].strip() or (request.client.host if request.client else "unknown"),
expires_at=datetime.now(timezone.utc)+expires_delta,
)
self.jwt_db_actions.create_token(RefreshTokensCreate.model_dump(new_token_record))
'''update old token to deactivate it and assign replaced_by'''
self.jwt_db_actions.update_token(old_jti, new_jti)
return new_refresh_token
def logout(self, refresh_token:str)->bool:
'''decode current refresh token'''
payload=self.jwt_service.jwt_decode(refresh_token)
if (jti:=payload.get("jti")) is None:
raise self.error.credentials_error(detail="Invalid Refresh Token")
try:
jti=UUID(jti)
except (ValueError, TypeError):
raise self.error.credentials_error(detail="Jwt token is incorrect")
current_token = self.jwt_db_actions.get_token_by_id(jti)
if current_token is None:
raise self.error.not_found_error(detail="Refresh Token Not Found")
'''logout by assigning revoked flag'''
return self.jwt_db_actions.logout(jti)
def login(self, form_data_email:str, form_data_password:str, request:Request)->tuple[str, str]:
'''create access and refresh tokens'''
access_token=self.create_access_token(form_data_email=form_data_email, form_data_password=form_data_password)
refresh_token=self.create_refresh_token(form_data_password=form_data_password, form_data_email=form_data_email,request=request)
return (access_token, refresh_token)
auth=CurrentUser()