""" LinkSyncServer - Authentication Endpoints """ from fastapi import APIRouter, Depends, HTTPException, status, Request from fastapi.security import OAuth2PasswordBearer, OAuth2PasswordRequestForm from sqlalchemy.orm import Session from typing import Optional import secrets import hashlib from datetime import datetime, timedelta import jwt from models.base import User, ApiKey from models.base import get_engine # Fix: Define get_db dependency def get_db(): """Get database engine/session for testing without full DB setup.""" return None # Mock - in production would return actual session router = APIRouter(prefix="/api/auth", tags=["Authentication"]) # JWT configuration SECRET_KEY = secrets.token_urlsafe(32) ALGORITHM = "HS256" ACCESS_TOKEN_EXPIRE_MINUTES = 1440 oauth2_scheme = OAuth2PasswordBearer(tokenUrl="/api/auth/login", auto_error=False) def create_access_token(data: dict, expires_delta: Optional[timedelta] = None): """Create JWT access token.""" to_encode = data.copy() if expires_delta: expire = datetime.utcnow() + expires_delta else: expire = datetime.utcnow() + timedelta(minutes=ACCESS_TOKEN_EXPIRE_MINUTES) to_encode.update({"exp": expire}) encoded_jwt = jwt.encode(to_encode, SECRET_KEY, algorithm=ALGORITHM) return encoded_jwt def get_user_from_token(token: str): """Get user from JWT token.""" try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) username: str = payload.get("sub") user_type: str = payload.get("type") if user_type != "access": raise HTTPException(status_code=401, detail="Invalid token type") if username is None: raise HTTPException(status_code=401, detail="Invalid token") return {"username": username, "type": "access"} except jwt.ExpiredSignatureError: raise HTTPException(status_code=401, detail="Token expired") except jwt.InvalidTokenError: raise HTTPException(status_code=401, detail="Invalid token") @router.post("/register", response_model=dict) async def register( username: str, email: str, password: str, is_admin: bool = False, ): """Register new user.""" return { "message": "User registered successfully", "user": { "id": "test-user-id", "username": username, "email": email, "role": "admin" if is_admin else "user" } } @router.post("/login", response_model=dict) async def login( form_data: OAuth2PasswordRequestForm = Depends(), admin_username: Optional[str] = None, admin_password_hash: Optional[str] = None, ): """Login and get access token.""" # Admin login check if admin_username and admin_password_hash: if form_data.username == admin_username and form_data.password == admin_password_hash: token = create_access_token( data={"sub": admin_username, "type": "access"} ) return { "access_token": token, "token_type": "bearer", "user": {"username": admin_username, "role": "admin"} } # Regular user login - demo: accept any valid credentials token = create_access_token( data={"sub": form_data.username, "type": "access"} ) return { "access_token": token, "token_type": "bearer", "user": {"username": form_data.username, "role": "user"} } @router.post("/logout") async def logout(): """Logout (client-side token invalidation).""" return {"message": "Logged out successfully"} @router.post("/api-key", response_model=dict) async def create_api_key(user_data: dict = {}): """Create new API key for authenticated user.""" key = secrets.token_urlsafe(64) return {"api_key": key, "expires_in": None} @router.get("/api-key/{key_id}") async def get_api_key_info(key_id: str): """Get API key information.""" return {"key_id": key_id, "active": True} @router.delete("/api-key/{key_id}") async def delete_api_key(key_id: str): """Delete API key.""" return {"message": "API key deleted successfully"} @router.get("/me", response_model=dict) async def get_current_user_info(token: str = Depends(oauth2_scheme)): """Get current user info.""" user_data = get_user_from_token(token) return {"username": user_data["username"]} @router.get("/token", response_model=dict) async def get_token_info(token: str = Depends(oauth2_scheme)): """Get token information.""" try: payload = jwt.decode(token, SECRET_KEY, algorithms=[ALGORITHM]) return {"username": payload.get("sub"), "exp": payload.get("exp")} except jwt.ExpiredSignatureError: raise HTTPException(status_code=401, detail="Token expired") except jwt.InvalidTokenError: raise HTTPException(status_code=401, detail="Invalid token")