164 lines
No EOL
5.4 KiB
Python
164 lines
No EOL
5.4 KiB
Python
"""
|
|
Routes for Google authentication.
|
|
"""
|
|
|
|
from fastapi import APIRouter, HTTPException, Request, Response, status, Depends
|
|
from fastapi.responses import HTMLResponse, RedirectResponse, JSONResponse
|
|
import logging
|
|
import json
|
|
from typing import Dict, Any, Optional
|
|
from datetime import datetime, timedelta
|
|
from google.oauth2.credentials import Credentials
|
|
from google_auth_oauthlib.flow import Flow
|
|
from google.auth.transport.requests import Request
|
|
|
|
from modules.shared.configuration import APP_CONFIG
|
|
from modules.interfaces.serviceAppClass import getInterface
|
|
from modules.interfaces.serviceAppModel import AuthAuthority
|
|
from modules.interfaces.serviceAppTokens import GoogleToken, saveToken
|
|
from modules.security.auth import getCurrentUser, limiter
|
|
|
|
# Configure logger
|
|
logger = logging.getLogger(__name__)
|
|
|
|
# Create router
|
|
router = APIRouter(
|
|
prefix="/api/google",
|
|
tags=["Security Google"],
|
|
responses={
|
|
404: {"description": "Not found"},
|
|
400: {"description": "Bad request"},
|
|
401: {"description": "Unauthorized"},
|
|
403: {"description": "Forbidden"},
|
|
500: {"description": "Internal server error"}
|
|
}
|
|
)
|
|
|
|
# Google OAuth configuration
|
|
CLIENT_ID = APP_CONFIG.get("Service_GOOGLE_CLIENT_ID")
|
|
CLIENT_SECRET = APP_CONFIG.get("Service_GOOGLE_CLIENT_SECRET")
|
|
REDIRECT_URI = APP_CONFIG.get("Service_GOOGLE_REDIRECT_URI")
|
|
SCOPES = [
|
|
"https://www.googleapis.com/auth/gmail.readonly",
|
|
"https://www.googleapis.com/auth/userinfo.profile",
|
|
"https://www.googleapis.com/auth/userinfo.email"
|
|
]
|
|
|
|
@router.get("/login")
|
|
@limiter.limit("5/minute")
|
|
async def login():
|
|
"""Initiate Google login"""
|
|
try:
|
|
# Create OAuth flow
|
|
flow = Flow.from_client_config(
|
|
{
|
|
"web": {
|
|
"client_id": CLIENT_ID,
|
|
"client_secret": CLIENT_SECRET,
|
|
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
|
"token_uri": "https://oauth2.googleapis.com/token",
|
|
"redirect_uris": [REDIRECT_URI]
|
|
}
|
|
},
|
|
scopes=SCOPES
|
|
)
|
|
|
|
# Generate auth URL
|
|
auth_url, _ = flow.authorization_url(
|
|
access_type="offline",
|
|
include_granted_scopes="true"
|
|
)
|
|
|
|
return RedirectResponse(auth_url)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error initiating Google login: {str(e)}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Failed to initiate Google login: {str(e)}"
|
|
)
|
|
|
|
@router.get("/auth/callback")
|
|
async def auth_callback(code: str, request: Request):
|
|
"""Handle Google OAuth callback"""
|
|
try:
|
|
# Create OAuth flow
|
|
flow = Flow.from_client_config(
|
|
{
|
|
"web": {
|
|
"client_id": CLIENT_ID,
|
|
"client_secret": CLIENT_SECRET,
|
|
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
|
|
"token_uri": "https://oauth2.googleapis.com/token",
|
|
"redirect_uris": [REDIRECT_URI]
|
|
}
|
|
},
|
|
scopes=SCOPES,
|
|
redirect_uri=REDIRECT_URI
|
|
)
|
|
|
|
# Exchange code for token
|
|
flow.fetch_token(code=code)
|
|
credentials = flow.credentials
|
|
|
|
# Create token data
|
|
token_data = {
|
|
"access_token": credentials.token,
|
|
"refresh_token": credentials.refresh_token,
|
|
"token_type": credentials.token_type,
|
|
"expires_at": credentials.expiry.timestamp()
|
|
}
|
|
|
|
# Save token data
|
|
appInterface = getInterface()
|
|
saveToken(appInterface, "Google", token_data)
|
|
|
|
# Return success page with token data
|
|
return HTMLResponse(
|
|
content=f"""
|
|
<html>
|
|
<head><title>Authentication Successful</title></head>
|
|
<body>
|
|
<script>
|
|
if (window.opener) {{
|
|
window.opener.postMessage({{
|
|
type: 'google_auth_success',
|
|
access_token: {json.dumps(credentials.token)},
|
|
token_data: {json.dumps(token_data)}
|
|
}}, '*');
|
|
}}
|
|
setTimeout(() => window.close(), 1000);
|
|
</script>
|
|
</body>
|
|
</html>
|
|
"""
|
|
)
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error in auth callback: {str(e)}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Authentication failed: {str(e)}"
|
|
)
|
|
|
|
@router.post("/logout")
|
|
@limiter.limit("30/minute")
|
|
async def logout(currentUser: Dict[str, Any] = Depends(getCurrentUser)):
|
|
"""Logout from Google"""
|
|
try:
|
|
# Get user interface
|
|
appInterface = getInterface()
|
|
|
|
# Revoke all sessions for the user
|
|
appInterface.revokeAllUserSessions(currentUser.get("id"))
|
|
|
|
return JSONResponse({
|
|
"message": "Successfully logged out from Google"
|
|
})
|
|
|
|
except Exception as e:
|
|
logger.error(f"Error during logout: {str(e)}")
|
|
raise HTTPException(
|
|
status_code=status.HTTP_500_INTERNAL_SERVER_ERROR,
|
|
detail=f"Logout failed: {str(e)}"
|
|
) |