From b9a7822d5c6a3767a1ec37999b4cccead95876bd Mon Sep 17 00:00:00 2001 From: Alix JEUDI--LEMOINE Date: Thu, 19 Jun 2025 00:47:53 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Added=20refresh=20token=20handling?= =?UTF-8?q?=20+=20updated=20models=20and=20authentication=20routes=20to=20?= =?UTF-8?q?include=20the=20refresh=20token.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/dto/__init__.py | 3 +- app/dto/token.py | 4 +++ app/models/token.py | 1 + app/routes/auth.py | 69 +++++++++++++++++++++++++++++++-------------- 4 files changed, 55 insertions(+), 22 deletions(-) create mode 100644 app/dto/token.py diff --git a/app/dto/__init__.py b/app/dto/__init__.py index 02d8375..f18b9fb 100644 --- a/app/dto/__init__.py +++ b/app/dto/__init__.py @@ -1,3 +1,4 @@ from .FriendAddDTO import FriendAddDTO from .user import UserDTO, UserRegisterDTO, UserAdminDTO -from .pin import PinDTO, PinShareDTO \ No newline at end of file +from .pin import PinDTO, PinShareDTO +from .token import RefreshTokenDTO \ No newline at end of file diff --git a/app/dto/token.py b/app/dto/token.py new file mode 100644 index 0000000..78d25b0 --- /dev/null +++ b/app/dto/token.py @@ -0,0 +1,4 @@ +from pydantic import BaseModel + +class RefreshTokenDTO(BaseModel): + refresh_token: str \ No newline at end of file diff --git a/app/models/token.py b/app/models/token.py index 9f3e26b..4ebaff5 100644 --- a/app/models/token.py +++ b/app/models/token.py @@ -1,6 +1,7 @@ from pydantic import BaseModel class Token(BaseModel): + refresh_token: str access_token: str token_type: str user_id: str diff --git a/app/routes/auth.py b/app/routes/auth.py index e7ea1e7..e3f33e8 100644 --- a/app/routes/auth.py +++ b/app/routes/auth.py @@ -6,8 +6,8 @@ import pymongo import app.config as config from app.models import User, Token, HTTPError -from app.dto import UserRegisterDTO -from app.routes.utils import get_current_user, create_access_token +from app.dto import UserRegisterDTO, RefreshTokenDTO +from app.routes.utils import get_current_user, create_access_token, create_refresh_token, get_current_user_from_refresh_token from app.utils import get_password_hash, verify_password # Database setup @@ -20,6 +20,26 @@ auth_router = APIRouter( tags=["Auth"] ) +def create_tokens(user: User): + access_token_expires = timedelta(minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES) + access_token = create_access_token( + data={ + "sub": user["username"], + "is_admin": user["is_admin"] + }, + expires_delta=access_token_expires + ) + refresh_token_expires = timedelta(minutes=config.REFRESH_TOKEN_EXPIRE_MINUTES) + refresh_token = create_refresh_token( + data={ + "sub": user["username"], + "is_admin": user["is_admin"] + }, + expires_delta=refresh_token_expires + ) + return access_token, refresh_token + + @auth_router.post( path="/register", response_model=Token, @@ -34,23 +54,18 @@ async def register(user: UserRegisterDTO): ) hashed_password = get_password_hash(user.password) - user_id = users_collection.insert_one({ - "username": user.username, + user = { + "username": user.username, "password": hashed_password, "is_admin": False - }) + } + user_id = users_collection.insert_one(user) - access_token_expires = timedelta(minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES) - access_token = create_access_token( - data={ - "sub": user.username, - "is_admin": False - }, - expires_delta=access_token_expires - ) + access_token, refresh_token = create_tokens(user) return { "access_token": access_token, + "refresh_token": refresh_token, "token_type": "bearer", "user_id": str(user_id.inserted_id), "is_admin": False @@ -70,25 +85,37 @@ async def login(form_data: OAuth2PasswordRequestForm = Depends()): headers={"WWW-Authenticate": "Bearer"}, ) - access_token_expires = timedelta(minutes=config.ACCESS_TOKEN_EXPIRE_MINUTES) - access_token = create_access_token( - data={ - "sub": form_data.username, - "is_admin": user.get("is_admin", False) - }, - expires_delta=access_token_expires - ) + access_token, refresh_token = create_tokens(user) return { "access_token": access_token, + "refresh_token": refresh_token, "token_type": "bearer", "user_id": str(user["_id"]), "is_admin": user.get("is_admin", False) } +@auth_router.post( + path="/refresh-token", + response_model=Token, + responses={401: {"model": HTTPError}} +) +async def refresh(refresh_data: RefreshTokenDTO): + current_user = get_current_user_from_refresh_token(refresh_data.refresh_token) + new_access_token, new_refresh_token = create_tokens(current_user) + + return { + "access_token": new_access_token, + "refresh_token": new_refresh_token, + "token_type": "bearer", + "user_id": str(current_user["_id"]), + "is_admin": current_user["is_admin"] + } + @auth_router.get( path="/logout", responses={401: {"model": HTTPError}} ) async def logout(current_user: User = Depends(get_current_user)): return {"message": "Logged out"} +