Files
MOHPortalTest-AllAgents-All…/qwen/python/merchants_of_hope/services/auth_service.py
2025-10-24 16:29:40 -05:00

108 lines
3.9 KiB
Python

"""
Authentication service with OIDC and social media support
"""
from datetime import datetime, timedelta
from typing import Optional
from jose import jwt
from fastapi import HTTPException, status
from sqlalchemy.orm import Session
from ..config.settings import settings
from ..models import User
from ..database import SessionLocal
def create_access_token(data: dict, expires_delta: Optional[timedelta] = None):
"""Create a JWT access token"""
to_encode = data.copy()
if expires_delta:
expire = datetime.utcnow() + expires_delta
else:
expire = datetime.utcnow() + timedelta(minutes=settings.ACCESS_TOKEN_EXPIRE_MINUTES)
to_encode.update({"exp": expire})
encoded_jwt = jwt.encode(to_encode, settings.SECRET_KEY, algorithm=settings.ALGORITHM)
return encoded_jwt
def verify_token(token: str) -> dict:
"""Verify a JWT token and return the payload"""
try:
payload = jwt.decode(token, settings.SECRET_KEY, algorithms=[settings.ALGORITHM])
return payload
except jwt.JWTError:
raise HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
def authenticate_user(db: Session, username: str, password: str) -> Optional[User]:
"""Authenticate a user with username and password"""
user = db.query(User).filter(User.username == username).first()
if not user or not verify_password_correct(password, user.hashed_password):
return None
if not user.is_active:
return None
return user
async def get_oidc_config():
"""Get OIDC configuration"""
return {
"issuer": settings.OIDC_ISSUER,
"authorization_endpoint": f"{settings.OIDC_ISSUER}/authorize" if settings.OIDC_ISSUER else None,
"token_endpoint": f"{settings.OIDC_ISSUER}/token" if settings.OIDC_ISSUER else None,
"userinfo_endpoint": f"{settings.OIDC_ISSUER}/userinfo" if settings.OIDC_ISSUER else None,
"jwks_uri": f"{settings.OIDC_ISSUER}/.well-known/jwks.json" if settings.OIDC_ISSUER else None,
"client_id": settings.OIDC_CLIENT_ID,
"redirect_uri": settings.OIDC_REDIRECT_URI,
}
async def handle_oidc_callback(code: str) -> User:
"""
Handle OIDC callback and return or create a user
This is a simplified implementation - in a real app you'd exchange the code
for tokens and fetch user info from the OIDC provider
"""
# In a real implementation, you would:
# 1. Exchange the authorization code for access/id tokens
# 2. Validate the ID token
# 3. Fetch user info from the userinfo endpoint
# 4. Create or update the user in your database
# 5. Return the user object
# For now, return a placeholder - this would be replaced with real OIDC logic
raise NotImplementedError("OIDC callback handling not fully implemented")
async def handle_social_login(provider: str, access_token: str) -> User:
"""
Handle social media login (Google, Facebook, etc.)
This is a simplified implementation
"""
# In a real implementation, you would:
# 1. Validate the access token with the social provider
# 2. Fetch user info from the provider's API
# 3. Create or update the user in your database
# 4. Return the user object
# For now, return a placeholder - this would be replaced with real social login logic
raise NotImplementedError("Social login handling not fully implemented")
def hash_password(password: str) -> str:
"""
Hash a password using bcrypt
"""
from ..utils.security import get_password_hash
return get_password_hash(password)
def verify_password_correct(plain_password: str, hashed_password: str) -> bool:
"""
Verify a plain password against its hash
"""
from ..utils.security import verify_password as verify_password_util
return verify_password_util(plain_password, hashed_password)