feat: backend FastAPI Smart App City — auth JWT, IoT, GIS, notifications, reporting

This commit is contained in:
Eric FELIXINE
2026-06-01 14:47:05 -04:00
parent 31334b5ce5
commit 08ca495bde
24 changed files with 2904 additions and 0 deletions

View File

@@ -0,0 +1,67 @@
"""JWT authentication utilities — token creation, validation, and dependency."""
from datetime import datetime, timedelta, timezone
from typing import Optional
import jwt
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from app.config import settings
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/auth/login")
SECRET_KEY = settings.JWT_SECRET
ALGORITHM = settings.JWT_ALGORITHM
ACCESS_TOKEN_EXPIRE_MINUTES = settings.ACCESS_TOKEN_EXPIRE_MINUTES
REFRESH_TOKEN_EXPIRE_DAYS = settings.REFRESH_TOKEN_EXPIRE_DAYS
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None) -> str:
"""Create a JWT access token.
Encodes *data* into a JWT signed with SECRET_KEY.
If *expires_delta* is omitted, ACCESS_TOKEN_EXPIRE_MINUTES is used.
"""
to_encode = data.copy()
expire = datetime.now(timezone.utc) + (
expires_delta or timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES)
)
to_encode.update({"exp": expire, "type": "access"})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
def create_refresh_token(data: dict) -> str:
"""Create a JWT refresh token valid for REFRESH_TOKEN_EXPIRE_DAYS days."""
to_encode = data.copy()
expire = datetime.now(timezone.utc) + timedelta(days=REFRESH_TOKEN_EXPIRE_DAYS)
to_encode.update({"exp": expire, "type": "refresh"})
return jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM)
def verify_token(token: str) -> dict:
"""Decode and verify a JWT.
Raises HTTPException 401 if the token is invalid or expired.
"""
try:
payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM])
return payload
except jwt.ExpiredSignatureError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Token has expired",
headers={"WWW-Authenticate": "Bearer"},
)
except jwt.InvalidTokenError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Invalid token",
headers={"WWW-Authenticate": "Bearer"},
)
async def get_current_user(token: str = Depends(oauth2_scheme)) -> dict:
"""FastAPI dependency that extracts the current user from a Bearer JWT."""
payload = verify_token(token)
return payload