Migrate to FastAPI JWT auth
This commit is contained in:
0
api/app/__init__.py
Normal file
0
api/app/__init__.py
Normal file
40
api/app/api.py
Normal file
40
api/app/api.py
Normal file
@@ -0,0 +1,40 @@
|
||||
import logging
|
||||
from contextlib import asynccontextmanager
|
||||
|
||||
from fastapi import FastAPI, Request
|
||||
|
||||
from mongoengine import connect
|
||||
|
||||
from app.config import get_settings
|
||||
from database.utils import create_admin_user
|
||||
from routes import users, flights
|
||||
|
||||
logger = logging.getLogger("api")
|
||||
|
||||
logging.basicConfig(format='%(asctime)s - %(levelname)s: %(message)s', level=logging.DEBUG)
|
||||
|
||||
|
||||
async def connect_to_db():
|
||||
# Connect to MongoDB
|
||||
settings = get_settings()
|
||||
try:
|
||||
connected = connect(settings.db_name, host=settings.db_uri, username=settings.db_user,
|
||||
password=settings.db_pwd, authentication_source=settings.db_name)
|
||||
if connected:
|
||||
logging.info("Connected to database %s", settings.db_name)
|
||||
# Create default admin user if it doesn't exist
|
||||
create_admin_user()
|
||||
except ConnectionError:
|
||||
logger.error("Failed to connect to MongoDB")
|
||||
raise ConnectionError
|
||||
|
||||
|
||||
# Initialize FastAPI
|
||||
app = FastAPI()
|
||||
app.include_router(users.router)
|
||||
app.include_router(flights.router)
|
||||
|
||||
|
||||
@app.on_event("startup")
|
||||
async def startup():
|
||||
await connect_to_db()
|
25
api/app/config.py
Normal file
25
api/app/config.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from functools import lru_cache
|
||||
|
||||
from pydantic_settings import BaseSettings, SettingsConfigDict
|
||||
|
||||
|
||||
class Settings(BaseSettings):
|
||||
model_config = SettingsConfigDict(env_file=".env", env_file_encoding="utf-8")
|
||||
|
||||
db_uri: str = "localhost"
|
||||
db_name: str = "tailfin"
|
||||
|
||||
db_user: str
|
||||
db_pwd: str
|
||||
|
||||
access_token_expire_minutes: int = 30
|
||||
refresh_token_expire_minutes: int = 60 * 24 * 7
|
||||
|
||||
jwt_algorithm: str = "HS256"
|
||||
jwt_secret_key: str = "please-change-me"
|
||||
jwt_refresh_secret_key: str = "change-me-i-beg-of-you"
|
||||
|
||||
|
||||
@lru_cache
|
||||
def get_settings():
|
||||
return Settings()
|
73
api/app/deps.py
Normal file
73
api/app/deps.py
Normal file
@@ -0,0 +1,73 @@
|
||||
from datetime import datetime
|
||||
from typing import Annotated
|
||||
|
||||
from fastapi import Depends, HTTPException
|
||||
from fastapi.security import OAuth2PasswordBearer
|
||||
from jose import jwt
|
||||
from mongoengine import DoesNotExist
|
||||
from pydantic import ValidationError
|
||||
|
||||
from app.config import get_settings, Settings
|
||||
from database.models import User, TokenBlacklist
|
||||
from schemas import GetSystemUserSchema, TokenPayload, AuthLevel
|
||||
|
||||
reusable_oath = OAuth2PasswordBearer(
|
||||
tokenUrl="/login",
|
||||
scheme_name="JWT"
|
||||
)
|
||||
|
||||
|
||||
async def get_current_user(settings: Annotated[Settings, Depends(get_settings)],
|
||||
token: str = Depends(reusable_oath)) -> GetSystemUserSchema:
|
||||
try:
|
||||
payload = jwt.decode(
|
||||
token, settings.jwt_secret_key, algorithms=[settings.jwt_algorithm]
|
||||
)
|
||||
token_data = TokenPayload(**payload)
|
||||
|
||||
if datetime.fromtimestamp(token_data.exp) < datetime.now():
|
||||
raise HTTPException(401, "Token expired", {"WWW-Authenticate": "Bearer"})
|
||||
except (jwt.JWTError, ValidationError):
|
||||
raise HTTPException(403, "Could not validate credentials", {"WWW-Authenticate": "Bearer"})
|
||||
|
||||
try:
|
||||
TokenBlacklist.objects.get(token=token)
|
||||
raise HTTPException(403, "Token expired", {"WWW-Authenticate": "Bearer"})
|
||||
except DoesNotExist:
|
||||
try:
|
||||
user = User.objects.get(id=token_data.sub)
|
||||
except DoesNotExist:
|
||||
raise HTTPException(404, "Could not find user")
|
||||
|
||||
return GetSystemUserSchema(id=str(user.id), username=user.username, level=user.level, password=user.password)
|
||||
|
||||
|
||||
async def get_current_user_token(settings: Annotated[Settings, Depends(get_settings)],
|
||||
token: str = Depends(reusable_oath)) -> (GetSystemUserSchema, str):
|
||||
try:
|
||||
payload = jwt.decode(
|
||||
token, settings.jwt_secret_key, algorithms=[settings.jwt_algorithm]
|
||||
)
|
||||
token_data = TokenPayload(**payload)
|
||||
|
||||
if datetime.fromtimestamp(token_data.exp) < datetime.now():
|
||||
raise HTTPException(401, "Token expired", {"WWW-Authenticate": "Bearer"})
|
||||
except (jwt.JWTError, ValidationError):
|
||||
raise HTTPException(403, "Could not validate credentials", {"WWW-Authenticate": "Bearer"})
|
||||
|
||||
try:
|
||||
TokenBlacklist.objects.get(token=token)
|
||||
raise HTTPException(403, "Token expired", {"WWW-Authenticate": "Bearer"})
|
||||
except DoesNotExist:
|
||||
try:
|
||||
user = User.objects.get(id=token_data.sub)
|
||||
except DoesNotExist:
|
||||
raise HTTPException(404, "Could not find user")
|
||||
|
||||
return GetSystemUserSchema(id=str(user.id), username=user.username, level=user.level,
|
||||
password=user.password), token
|
||||
|
||||
|
||||
async def admin_required(user: Annotated[GetSystemUserSchema, Depends(get_current_user)]):
|
||||
if user.level < AuthLevel.ADMIN:
|
||||
raise HTTPException(403, "Access unauthorized")
|
Reference in New Issue
Block a user