feat: add fetch_google_user_email and update Google OAuth routes

This commit is contained in:
CREDO23 2026-01-07 08:15:56 +02:00
parent 4c6a782cec
commit 932222bff1
4 changed files with 65 additions and 25 deletions

View file

@ -6,6 +6,7 @@ Allows fetching emails from Gmail mailbox using Google OAuth credentials.
import base64
import json
import logging
import re
from typing import Any
@ -21,6 +22,34 @@ from app.db import (
SearchSourceConnectorType,
)
logger = logging.getLogger(__name__)
def fetch_google_user_email(credentials: Credentials) -> str | None:
"""
Fetch user email from Gmail API using Google credentials.
Uses the Gmail users.getProfile endpoint which returns the authenticated
user's email address.
Args:
credentials: Google OAuth Credentials object (not encrypted)
Returns:
User's email address or None if fetch fails
"""
try:
service = build("gmail", "v1", credentials=credentials)
profile = service.users().getProfile(userId="me").execute()
email = profile.get("emailAddress")
if email:
logger.debug(f"Fetched Google user email: {email}")
return email
return None
except Exception as e:
logger.warning(f"Error fetching Google user email: {e!s}")
return None
class GoogleGmailConnector:
"""Class for retrieving emails from Gmail using Google OAuth credentials."""

View file

@ -15,6 +15,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from app.config import config
from app.connectors.google_gmail_connector import fetch_google_user_email
from app.db import (
SearchSourceConnector,
SearchSourceConnectorType,
@ -22,8 +23,8 @@ from app.db import (
get_async_session,
)
from app.users import current_active_user
from app.utils.connector_naming import generate_unique_connector_name
from app.utils.oauth_security import OAuthStateManager, TokenEncryption
from app.utils.connector_naming import generate_unique_connector_name, extract_identifier_from_credentials
logger = logging.getLogger(__name__)
@ -173,6 +174,9 @@ async def calendar_callback(
creds = flow.credentials
creds_dict = json.loads(creds.to_json())
# Fetch user email before encrypting credentials
user_email = fetch_google_user_email(creds)
# Encrypt sensitive credentials before storing
token_encryption = get_token_encryption()
@ -192,14 +196,13 @@ async def calendar_callback(
creds_dict["_token_encrypted"] = True
try:
# Extract unique identifier from connector credentials
connector_identifier = extract_identifier_from_credentials(
SearchSourceConnectorType.GOOGLE_CALENDAR_CONNECTOR, creds_dict
)
# Generate a unique, user-friendly connector name from credentials/account info
connector_name = generate_unique_connector_name(
SearchSourceConnectorType.GOOGLE_CALENDAR_CONNECTOR, connector_identifier
# Generate a unique, user-friendly connector name
connector_name = await generate_unique_connector_name(
session,
SearchSourceConnectorType.GOOGLE_CALENDAR_CONNECTOR,
space_id,
user_id,
user_email,
)
db_connector = SearchSourceConnector(
name=connector_name,

View file

@ -29,6 +29,7 @@ from app.connectors.google_drive import (
get_start_page_token,
list_folder_contents,
)
from app.connectors.google_gmail_connector import fetch_google_user_email
from app.db import (
SearchSourceConnector,
SearchSourceConnectorType,
@ -36,8 +37,8 @@ from app.db import (
get_async_session,
)
from app.users import current_active_user
from app.utils.connector_naming import generate_unique_connector_name
from app.utils.oauth_security import OAuthStateManager, TokenEncryption
from app.utils.connector_naming import generate_unique_connector_name, extract_identifier_from_credentials
# Relax token scope validation for Google OAuth
os.environ["OAUTHLIB_RELAX_TOKEN_SCOPE"] = "1"
@ -228,6 +229,9 @@ async def drive_callback(
creds = flow.credentials
creds_dict = json.loads(creds.to_json())
# Fetch user email before encrypting credentials
user_email = fetch_google_user_email(creds)
# Encrypt sensitive credentials before storing
token_encryption = get_token_encryption()
@ -246,13 +250,13 @@ async def drive_callback(
# Mark that credentials are encrypted for backward compatibility
creds_dict["_token_encrypted"] = True
# Extract unique identifier from connector credentials
connector_identifier = extract_identifier_from_credentials(
SearchSourceConnectorType.GOOGLE_DRIVE_CONNECTOR, creds_dict
)
# Generate a unique, user-friendly connector name from credentials/account info
connector_name = generate_unique_connector_name(
SearchSourceConnectorType.GOOGLE_DRIVE_CONNECTOR, connector_identifier
# Generate a unique, user-friendly connector name
connector_name = await generate_unique_connector_name(
session,
SearchSourceConnectorType.GOOGLE_DRIVE_CONNECTOR,
space_id,
user_id,
user_email,
)
db_connector = SearchSourceConnector(

View file

@ -15,6 +15,7 @@ from sqlalchemy.ext.asyncio import AsyncSession
from sqlalchemy.future import select
from app.config import config
from app.connectors.google_gmail_connector import fetch_google_user_email
from app.db import (
SearchSourceConnector,
SearchSourceConnectorType,
@ -22,8 +23,8 @@ from app.db import (
get_async_session,
)
from app.users import current_active_user
from app.utils.connector_naming import generate_unique_connector_name
from app.utils.oauth_security import OAuthStateManager, TokenEncryption
from app.utils.connector_naming import generate_unique_connector_name, extract_identifier_from_credentials
logger = logging.getLogger(__name__)
@ -204,6 +205,9 @@ async def gmail_callback(
creds = flow.credentials
creds_dict = json.loads(creds.to_json())
# Fetch user email before encrypting credentials
user_email = fetch_google_user_email(creds)
# Encrypt sensitive credentials before storing
token_encryption = get_token_encryption()
@ -223,13 +227,13 @@ async def gmail_callback(
creds_dict["_token_encrypted"] = True
try:
# Extract unique identifier from connector credentials
connector_identifier = extract_identifier_from_credentials(
SearchSourceConnectorType.GOOGLE_GMAIL_CONNECTOR, creds_dict
)
# Generate a unique, user-friendly connector name from credentials/account info
connector_name = generate_unique_connector_name(
SearchSourceConnectorType.GOOGLE_GMAIL_CONNECTOR, connector_identifier
# Generate a unique, user-friendly connector name
connector_name = await generate_unique_connector_name(
session,
SearchSourceConnectorType.GOOGLE_GMAIL_CONNECTOR,
space_id,
user_id,
user_email,
)
db_connector = SearchSourceConnector(
name=connector_name,