the middle of the idiots
This commit is contained in:
		@@ -1,17 +1,18 @@
 | 
			
		||||
"""
 | 
			
		||||
Authentication API routes
 | 
			
		||||
"""
 | 
			
		||||
from fastapi import APIRouter, Depends, HTTPException, status
 | 
			
		||||
from fastapi import APIRouter, Depends, HTTPException, status, Request
 | 
			
		||||
from fastapi.security import HTTPBearer
 | 
			
		||||
from datetime import datetime, timedelta
 | 
			
		||||
from typing import Optional
 | 
			
		||||
import jwt
 | 
			
		||||
from jose import jwt
 | 
			
		||||
from pydantic import BaseModel
 | 
			
		||||
from sqlalchemy.orm import Session
 | 
			
		||||
 | 
			
		||||
from ..config.settings import settings
 | 
			
		||||
from ..database import SessionLocal
 | 
			
		||||
from ..models import User
 | 
			
		||||
from ...config.settings import settings
 | 
			
		||||
from ...database import SessionLocal
 | 
			
		||||
from ...models import User
 | 
			
		||||
from ...services.auth_service import authenticate_user, create_access_token, get_oidc_config, handle_oidc_callback
 | 
			
		||||
 | 
			
		||||
router = APIRouter()
 | 
			
		||||
security = HTTPBearer()
 | 
			
		||||
@@ -21,6 +22,14 @@ class Token(BaseModel):
 | 
			
		||||
    access_token: str
 | 
			
		||||
    token_type: str
 | 
			
		||||
 | 
			
		||||
    class Config:
 | 
			
		||||
        json_schema_extra = {
 | 
			
		||||
            "example": {
 | 
			
		||||
                "access_token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...",
 | 
			
		||||
                "token_type": "bearer"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
class TokenData(BaseModel):
 | 
			
		||||
    username: Optional[str] = None
 | 
			
		||||
    tenant_id: Optional[str] = None
 | 
			
		||||
@@ -29,6 +38,14 @@ class UserLogin(BaseModel):
 | 
			
		||||
    username: str
 | 
			
		||||
    password: str
 | 
			
		||||
 | 
			
		||||
    class Config:
 | 
			
		||||
        json_schema_extra = {
 | 
			
		||||
            "example": {
 | 
			
		||||
                "username": "johndoe",
 | 
			
		||||
                "password": "securepassword"
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
 | 
			
		||||
    to_encode = data.copy()
 | 
			
		||||
    if expires_delta:
 | 
			
		||||
@@ -41,20 +58,13 @@ def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
 | 
			
		||||
 | 
			
		||||
@router.post("/token", response_model=Token)
 | 
			
		||||
async def login_for_access_token(form_data: UserLogin, db: Session = Depends(SessionLocal)):
 | 
			
		||||
    # This is a simplified version - in a real app, you'd hash passwords
 | 
			
		||||
    user = db.query(User).filter(User.username == form_data.username).first()
 | 
			
		||||
    if not user or user.hashed_password != form_data.password:  # Simplified check
 | 
			
		||||
    user = authenticate_user(db, form_data.username, form_data.password)
 | 
			
		||||
    if not user:
 | 
			
		||||
        raise HTTPException(
 | 
			
		||||
            status_code=status.HTTP_401_UNAUTHORIZED,
 | 
			
		||||
            detail="Incorrect username or password",
 | 
			
		||||
            headers={"WWW-Authenticate": "Bearer"},
 | 
			
		||||
        )
 | 
			
		||||
    if not user.is_active:
 | 
			
		||||
        raise HTTPException(
 | 
			
		||||
            status_code=status.HTTP_401_UNAUTHORIZED,
 | 
			
		||||
            detail="Inactive user",
 | 
			
		||||
            headers={"WWW-Authenticate": "Bearer"},
 | 
			
		||||
        )
 | 
			
		||||
    
 | 
			
		||||
    access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
 | 
			
		||||
    access_token = create_access_token(
 | 
			
		||||
@@ -64,10 +74,91 @@ async def login_for_access_token(form_data: UserLogin, db: Session = Depends(Ses
 | 
			
		||||
    return {"access_token": access_token, "token_type": "bearer"}
 | 
			
		||||
 | 
			
		||||
@router.get("/oidc-config")
 | 
			
		||||
async def get_oidc_config():
 | 
			
		||||
async def get_oidc_config_endpoint():
 | 
			
		||||
    """Get OIDC configuration"""
 | 
			
		||||
    return await get_oidc_config()
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@router.get("/oidc-login")
 | 
			
		||||
async def oidc_login():
 | 
			
		||||
    """Initiate OIDC login flow"""
 | 
			
		||||
    if not settings.OIDC_ISSUER or not settings.OIDC_CLIENT_ID or not settings.OIDC_REDIRECT_URI:
 | 
			
		||||
        raise HTTPException(status_code=500, detail="OIDC not properly configured")
 | 
			
		||||
    
 | 
			
		||||
    # Construct the authorization URL
 | 
			
		||||
    auth_url = (
 | 
			
		||||
        f"{settings.OIDC_ISSUER}/authorize?"
 | 
			
		||||
        f"client_id={settings.OIDC_CLIENT_ID}&"
 | 
			
		||||
        f"response_type=code&"
 | 
			
		||||
        f"redirect_uri={settings.OIDC_REDIRECT_URI}&"
 | 
			
		||||
        f"scope=openid profile email&"
 | 
			
		||||
        f"state=random_state_string"  # In real app, generate and store a proper state parameter
 | 
			
		||||
    )
 | 
			
		||||
    
 | 
			
		||||
    return {"auth_url": auth_url}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@router.get("/oidc-callback")
 | 
			
		||||
async def oidc_callback(code: str, state: str = None):
 | 
			
		||||
    """Handle OIDC callback"""
 | 
			
		||||
    # Verify state parameter in a real implementation
 | 
			
		||||
    
 | 
			
		||||
    # Handle the OIDC callback and return a user
 | 
			
		||||
    try:
 | 
			
		||||
        user = await handle_oidc_callback(code)
 | 
			
		||||
        # Create access token for the user
 | 
			
		||||
        access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
 | 
			
		||||
        access_token = create_access_token(
 | 
			
		||||
            data={"sub": user.username, "tenant_id": user.tenant_id},
 | 
			
		||||
            expires_delta=access_token_expires
 | 
			
		||||
        )
 | 
			
		||||
        return {"access_token": access_token, "token_type": "bearer"}
 | 
			
		||||
    except NotImplementedError:
 | 
			
		||||
        # For demo purposes, return a mock response
 | 
			
		||||
        return {
 | 
			
		||||
            "message": "OIDC callback received. In a real implementation, this would complete the login process.",
 | 
			
		||||
            "code": code,
 | 
			
		||||
            "state": state
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@router.get("/social-login/{provider}")
 | 
			
		||||
async def social_login(provider: str):
 | 
			
		||||
    """Initiate social media login flow (Google, Facebook, etc.)"""
 | 
			
		||||
    if provider not in ["google", "facebook", "github"]:
 | 
			
		||||
        raise HTTPException(status_code=400, detail="Unsupported social provider")
 | 
			
		||||
    
 | 
			
		||||
    # In a real implementation, redirect to the provider's OAuth URL
 | 
			
		||||
    # For demo purposes, return a mock URL
 | 
			
		||||
    auth_url = f"https://{provider}.com/oauth/authorize"  # This is just a placeholder
 | 
			
		||||
    return {
 | 
			
		||||
        "issuer": settings.OIDC_ISSUER,
 | 
			
		||||
        "client_id": settings.OIDC_CLIENT_ID,
 | 
			
		||||
        "redirect_uri": settings.OIDC_REDIRECT_URI
 | 
			
		||||
    }
 | 
			
		||||
        "message": f"Redirect user to {provider} for authentication",
 | 
			
		||||
        "auth_url": auth_url,
 | 
			
		||||
        "provider": provider
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@router.post("/social-login/{provider}/callback")
 | 
			
		||||
async def social_login_callback(provider: str, access_token: str):
 | 
			
		||||
    """Handle social media login callback"""
 | 
			
		||||
    if provider not in ["google", "facebook", "github"]:
 | 
			
		||||
        raise HTTPException(status_code=400, detail="Unsupported social provider")
 | 
			
		||||
    
 | 
			
		||||
    # In a real implementation, validate the access token and fetch user info
 | 
			
		||||
    # For demo purposes, return a mock response
 | 
			
		||||
    try:
 | 
			
		||||
        user = await handle_social_login(provider, access_token)
 | 
			
		||||
        # Create access token for the user
 | 
			
		||||
        access_token_expires = timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
 | 
			
		||||
        access_token = create_access_token(
 | 
			
		||||
            data={"sub": user.username, "tenant_id": user.tenant_id},
 | 
			
		||||
            expires_delta=access_token_expires
 | 
			
		||||
        )
 | 
			
		||||
        return {"access_token": access_token, "token_type": "bearer"}
 | 
			
		||||
    except NotImplementedError:
 | 
			
		||||
        # For demo purposes, return a mock response
 | 
			
		||||
        return {
 | 
			
		||||
            "message": "Social login callback received. In a real implementation, this would complete the login process.",
 | 
			
		||||
            "provider": provider,
 | 
			
		||||
            "access_token": access_token
 | 
			
		||||
        }
 | 
			
		||||
		Reference in New Issue
	
	Block a user