2023-04-01 16:19:12 +02:00
|
|
|
from passlib.context import CryptContext
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from sqlalchemy.ext.asyncio import async_sessionmaker
|
|
|
|
from jose import JWTError, jwt
|
|
|
|
from fastapi.security import OAuth2PasswordBearer
|
|
|
|
from fastapi import Depends, FastAPI, HTTPException
|
|
|
|
from fastapi_users.password import PasswordHelper
|
|
|
|
from sqlalchemy.ext.asyncio import async_sessionmaker
|
|
|
|
from typing import AsyncGenerator, Dict, Annotated, Optional
|
|
|
|
from datetime import datetime, timedelta
|
|
|
|
from .settings import Settings
|
|
|
|
from .domain.user import User
|
2023-04-02 14:38:22 +02:00
|
|
|
from .domain.token import RefreshToken
|
2023-04-01 16:19:12 +02:00
|
|
|
from .db import get_session
|
|
|
|
|
|
|
|
|
|
|
|
pwd_context = CryptContext(schemes=["bcrypt"], deprecated="auto")
|
2023-04-01 20:23:04 +02:00
|
|
|
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="api/token")
|
2023-04-01 16:19:12 +02:00
|
|
|
settings = Settings()
|
|
|
|
password_helper = PasswordHelper(pwd_context)
|
|
|
|
|
|
|
|
AsyncSessionDependency = Annotated[async_sessionmaker, Depends(get_session)]
|
|
|
|
TokenDependency = Annotated[str, Depends(oauth2_scheme)]
|
|
|
|
|
|
|
|
|
|
|
|
def verify_password(plain_password: str, hashed_password: str) -> bool:
|
|
|
|
return pwd_context.verify(plain_password, hashed_password)
|
|
|
|
|
|
|
|
|
|
|
|
def get_password_hash(password: str) -> str:
|
|
|
|
return pwd_context.hash(password)
|
|
|
|
|
|
|
|
|
|
|
|
async def authenticate_user(
|
|
|
|
session: AsyncSession, username: str, password: str
|
|
|
|
) -> AsyncGenerator[User, None]:
|
|
|
|
user = await User.get_by_username(session, username)
|
|
|
|
if not user:
|
|
|
|
return None
|
|
|
|
if not verify_password(password, user.hashed_password):
|
|
|
|
return None
|
|
|
|
return user
|
|
|
|
|
|
|
|
|
2023-04-02 14:38:22 +02:00
|
|
|
async def verify_refresh_token(
|
|
|
|
session: AsyncSession, token: str
|
2023-04-02 15:20:53 +02:00
|
|
|
) -> AsyncGenerator[RefreshToken, None]:
|
2023-04-02 14:38:22 +02:00
|
|
|
try:
|
|
|
|
payload = jwt.decode(
|
2023-04-02 14:51:08 +02:00
|
|
|
token, settings.REFRESH_SECRET_KEY, algorithms=[settings.ALGORITHM]
|
2023-04-02 14:38:22 +02:00
|
|
|
)
|
|
|
|
username: str = payload.get("sub")
|
|
|
|
if username is None:
|
|
|
|
return
|
|
|
|
except JWTError:
|
|
|
|
return
|
|
|
|
|
|
|
|
user = await User.get_by_username(session, username)
|
2023-04-02 15:20:53 +02:00
|
|
|
if user is None:
|
|
|
|
return
|
|
|
|
current_token = await RefreshToken.get_token(session, user.id, token)
|
|
|
|
if current_token is not None:
|
|
|
|
return current_token
|
2023-04-02 14:38:22 +02:00
|
|
|
|
|
|
|
|
|
|
|
def create_access_token(user: User) -> str:
|
2023-04-01 16:19:12 +02:00
|
|
|
expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
|
|
|
|
to_encode = {
|
|
|
|
"sub": user.username,
|
|
|
|
"exp": expire,
|
|
|
|
}
|
|
|
|
encoded_jwt = jwt.encode(
|
|
|
|
to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM
|
|
|
|
)
|
|
|
|
return encoded_jwt
|
|
|
|
|
|
|
|
|
2023-04-02 14:38:22 +02:00
|
|
|
async def create_refresh_token(session: AsyncSession, user: User) -> RefreshToken:
|
|
|
|
expire = datetime.utcnow() + timedelta(days=settings.REFRESH_TOKEN_EXPIRE_DAYS)
|
|
|
|
to_encode = {
|
|
|
|
"sub": user.username,
|
|
|
|
"exp": expire,
|
|
|
|
}
|
|
|
|
encoded_jwt = jwt.encode(
|
|
|
|
to_encode, settings.REFRESH_SECRET_KEY, algorithm=settings.ALGORITHM
|
|
|
|
)
|
|
|
|
return await RefreshToken.create(session, token=encoded_jwt, user_id=user.id)
|
|
|
|
|
|
|
|
|
2023-04-01 16:19:12 +02:00
|
|
|
async def get_current_user(
|
|
|
|
session: AsyncSessionDependency, token: TokenDependency
|
|
|
|
) -> User:
|
|
|
|
async with session() as session:
|
|
|
|
try:
|
|
|
|
payload = jwt.decode(
|
|
|
|
token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM]
|
|
|
|
)
|
|
|
|
username: str = payload.get("sub")
|
|
|
|
if username is None:
|
|
|
|
raise HTTPException(status_code=401, detail="Unathorized")
|
|
|
|
except JWTError:
|
|
|
|
raise HTTPException(status_code=401, detail="Unathorized")
|
|
|
|
|
|
|
|
return await User.get_by_username(session, username)
|