11from typing import Annotated , Dict
2- from datetime import timedelta
2+ from datetime import timedelta , datetime , timezone
33
4- from fastapi import Depends
4+ from fastapi import Response , Request , Depends
55from fastapi .security import OAuth2PasswordRequestForm
66from sqlalchemy .ext .asyncio import AsyncSession
77import fastapi
88
9+ from app .core .config import settings
910from app .core .db .database import async_get_db
10- from app .core .schemas import Token
11- from app .core .security import ACCESS_TOKEN_EXPIRE_MINUTES , create_access_token , authenticate_user
1211from app .core .exceptions .http_exceptions import UnauthorizedException
12+ from app .core .schemas import Token
13+ from app .core .security import (
14+ ACCESS_TOKEN_EXPIRE_MINUTES ,
15+ create_access_token ,
16+ authenticate_user ,
17+ create_refresh_token ,
18+ verify_token
19+ )
1320
1421router = fastapi .APIRouter (tags = ["login" ])
1522
1623@router .post ("/login" , response_model = Token )
1724async def login_for_access_token (
25+ response : Response ,
1826 form_data : Annotated [OAuth2PasswordRequestForm , Depends ()],
1927 db : Annotated [AsyncSession , Depends (async_get_db )]
2028) -> Dict [str , str ]:
@@ -30,5 +38,37 @@ async def login_for_access_token(
3038 access_token = await create_access_token (
3139 data = {"sub" : user ["username" ]}, expires_delta = access_token_expires
3240 )
41+
42+ refresh_token = await create_refresh_token (data = {"sub" : user ["username" ]})
43+ max_age = settings .REFRESH_TOKEN_EXPIRE_DAYS * 24 * 60 * 60
44+
45+ response .set_cookie (
46+ key = "refresh_token" ,
47+ value = refresh_token ,
48+ httponly = True ,
49+ secure = True ,
50+ samesite = 'Lax' ,
51+ max_age = max_age
52+ )
3353
34- return {"access_token" : access_token , "token_type" : "bearer" }
54+ return {
55+ "access_token" : access_token ,
56+ "token_type" : "bearer"
57+ }
58+
59+
60+ @router .post ("/refresh" )
61+ async def refresh_access_token (
62+ request : Request ,
63+ db : AsyncSession = Depends (async_get_db )
64+ ) -> Dict [str , str ]:
65+ refresh_token = request .cookies .get ("refresh_token" )
66+ if not refresh_token :
67+ raise UnauthorizedException ("Refresh token missing." )
68+
69+ user_data = await verify_token (refresh_token , db )
70+ if not user_data :
71+ raise UnauthorizedException ("Invalid refresh token." )
72+
73+ new_access_token = await create_access_token (data = {"sub" : user_data .username_or_email })
74+ return {"access_token" : new_access_token , "token_type" : "bearer" }
0 commit comments