From dcc7e52a860804f5c397244dd1b4354801689361 Mon Sep 17 00:00:00 2001 From: RaresKeY <158580472+RaresKeY@users.noreply.github.com> Date: Wed, 22 Jul 2026 12:59:22 +0200 Subject: [PATCH] fix(config): forward Google email OAuth settings through Compose (#5650) * fix(config): forward Google OAuth env vars into Docker container and document setup GOOGLE_OAUTH_CLIENT_ID, GOOGLE_OAUTH_CLIENT_SECRET, and GOOGLE_OAUTH_REDIRECT_URI were read by the app but never forwarded through docker-compose.yml's explicit environment allowlist, causing the "not set" error even when the vars existed in .env. Also adds a documented section to .env.example with step-by-step GCP setup instructions so users know where to get the credentials. Co-Authored-By: Claude Sonnet 4.6 * fix(config): cover OAuth in standalone compose files * test(config): parse OAuth compose service env * test(config): keep checkout skip wording neutral --------- Co-authored-by: TNTBA Co-authored-by: Claude Sonnet 4.6 --- .env.example | 21 ++++++++ docker-compose.gpu-amd.yml | 3 ++ docker-compose.gpu-nvidia.yml | 3 ++ docker-compose.yml | 3 ++ tests/test_email_oauth_docker_config.py | 68 +++++++++++++++++++++++++ 5 files changed, 98 insertions(+) create mode 100644 tests/test_email_oauth_docker_config.py diff --git a/.env.example b/.env.example index e96dd151c..d23276eb8 100644 --- a/.env.example +++ b/.env.example @@ -130,6 +130,27 @@ SEARXNG_INSTANCE=http://localhost:8080 # FASTEMBED_MODEL=sentence-transformers/all-MiniLM-L6-v2 # FASTEMBED_CACHE_PATH= # defaults to ~/.cache/fastembed +# ============================================================ +# Google OAuth2 (Google Workspace / .edu email accounts) +# ============================================================ +# Required to use the "Connect with Google" OAuth flow in email account setup. +# Create credentials at: console.cloud.google.com → APIs & Services → Credentials +# 1. Enable the Gmail API for your project. +# 2. Configure the OAuth consent screen (User Type: Internal for Workspace orgs). +# Add scopes: https://mail.google.com/ and email. +# 3. Create an OAuth 2.0 Client ID (type: Web application). +# Add your redirect URI: http://localhost:7000/api/email/oauth/google/callback +# (replace host/port for hosted installs). +# 4. Copy the Client ID and Client Secret below. +# +# GOOGLE_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com +# GOOGLE_OAUTH_CLIENT_SECRET=replace-with-client-secret +# +# Set this explicitly for HTTPS, reverse-proxy, or hosted deployments. The +# value must exactly match an authorized redirect URI in the Google client. +# Local HTTP setups may use the callback URL inferred by the application. +# GOOGLE_OAUTH_REDIRECT_URI=https://your-domain.com/api/email/oauth/google/callback + # ============================================================ # Misc # ============================================================ diff --git a/docker-compose.gpu-amd.yml b/docker-compose.gpu-amd.yml index 82e22e440..91e223e05 100644 --- a/docker-compose.gpu-amd.yml +++ b/docker-compose.gpu-amd.yml @@ -70,6 +70,9 @@ services: - DATA_BRAVE_API_KEY=${DATA_BRAVE_API_KEY:-} - GOOGLE_API_KEY=${GOOGLE_API_KEY:-} - GOOGLE_PSE_CX=${GOOGLE_PSE_CX:-} + - GOOGLE_OAUTH_CLIENT_ID=${GOOGLE_OAUTH_CLIENT_ID:-} + - GOOGLE_OAUTH_CLIENT_SECRET=${GOOGLE_OAUTH_CLIENT_SECRET:-} + - GOOGLE_OAUTH_REDIRECT_URI=${GOOGLE_OAUTH_REDIRECT_URI:-} - TAVILY_API_KEY=${TAVILY_API_KEY:-} - SERPER_API_KEY=${SERPER_API_KEY:-} # PUID / PGID — the user/group the container drops to before diff --git a/docker-compose.gpu-nvidia.yml b/docker-compose.gpu-nvidia.yml index 1b551c669..e8c2fd032 100644 --- a/docker-compose.gpu-nvidia.yml +++ b/docker-compose.gpu-nvidia.yml @@ -69,6 +69,9 @@ services: - DATA_BRAVE_API_KEY=${DATA_BRAVE_API_KEY:-} - GOOGLE_API_KEY=${GOOGLE_API_KEY:-} - GOOGLE_PSE_CX=${GOOGLE_PSE_CX:-} + - GOOGLE_OAUTH_CLIENT_ID=${GOOGLE_OAUTH_CLIENT_ID:-} + - GOOGLE_OAUTH_CLIENT_SECRET=${GOOGLE_OAUTH_CLIENT_SECRET:-} + - GOOGLE_OAUTH_REDIRECT_URI=${GOOGLE_OAUTH_REDIRECT_URI:-} - TAVILY_API_KEY=${TAVILY_API_KEY:-} - SERPER_API_KEY=${SERPER_API_KEY:-} # PUID / PGID — the user/group the container drops to before diff --git a/docker-compose.yml b/docker-compose.yml index cbeec1e37..b1f2c37ee 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -58,6 +58,9 @@ services: - DATA_BRAVE_API_KEY=${DATA_BRAVE_API_KEY:-} - GOOGLE_API_KEY=${GOOGLE_API_KEY:-} - GOOGLE_PSE_CX=${GOOGLE_PSE_CX:-} + - GOOGLE_OAUTH_CLIENT_ID=${GOOGLE_OAUTH_CLIENT_ID:-} + - GOOGLE_OAUTH_CLIENT_SECRET=${GOOGLE_OAUTH_CLIENT_SECRET:-} + - GOOGLE_OAUTH_REDIRECT_URI=${GOOGLE_OAUTH_REDIRECT_URI:-} - TAVILY_API_KEY=${TAVILY_API_KEY:-} - SERPER_API_KEY=${SERPER_API_KEY:-} # PUID / PGID — the user/group the container drops to before diff --git a/tests/test_email_oauth_docker_config.py b/tests/test_email_oauth_docker_config.py new file mode 100644 index 000000000..14e19aa0d --- /dev/null +++ b/tests/test_email_oauth_docker_config.py @@ -0,0 +1,68 @@ +"""Regression coverage for Google OAuth configuration in Docker Compose.""" + +from pathlib import Path + +import pytest +import yaml + + +ROOT = Path(__file__).resolve().parent.parent +COMPOSE_PATHS = tuple( + ROOT / name + for name in ( + "docker-compose.yml", + "docker-compose.gpu-nvidia.yml", + "docker-compose.gpu-amd.yml", + ) +) +ENV_EXAMPLE_PATH = ROOT / ".env.example" + + +def _env_example(): + if not ENV_EXAMPLE_PATH.exists(): + pytest.skip("this checkout does not include the optional .env.example file") + return ENV_EXAMPLE_PATH.read_text(encoding="utf-8") + + +def _odysseus_environment(path): + compose = yaml.safe_load(path.read_text(encoding="utf-8")) + return set(compose["services"]["odysseus"]["environment"]) + + +@pytest.mark.parametrize( + "key", + ( + "GOOGLE_OAUTH_CLIENT_ID", + "GOOGLE_OAUTH_CLIENT_SECRET", + "GOOGLE_OAUTH_REDIRECT_URI", + ), +) +def test_google_oauth_setting_is_forwarded(key): + expected = f"{key}=${{{key}:-}}" + for path in COMPOSE_PATHS: + assert expected in _odysseus_environment(path), path.name + + +@pytest.mark.parametrize( + "key", + ( + "GOOGLE_OAUTH_CLIENT_ID", + "GOOGLE_OAUTH_CLIENT_SECRET", + "GOOGLE_OAUTH_REDIRECT_URI", + ), +) +def test_google_oauth_setting_is_documented(key): + assert f"# {key}=" in _env_example() + + +def test_google_oauth_example_uses_a_neutral_secret_placeholder(): + env_example = _env_example() + assert "GOOGLE_OAUTH_CLIENT_SECRET=replace-with-client-secret" in env_example + assert "GOCSPX-" not in env_example + + +def test_redirect_documentation_covers_https_and_reverse_proxies(): + oauth_section = _env_example().split("# Google OAuth2", 1)[1].split("# Misc", 1)[0] + assert "HTTPS" in oauth_section + assert "reverse-proxy" in oauth_section + assert "exactly match an authorized redirect URI" in oauth_section