diff --git a/.env.template b/.env.template index 08151e602..58500cfc6 100644 --- a/.env.template +++ b/.env.template @@ -1,45 +1,76 @@ # AdCP Sales Agent - Environment Configuration # ============================================= -# Copy this file to .env: cp .env.template .env -# Then edit .env with your actual values. # +# SETUP PATHS: +# Path 1 (Mock/Testing): Set [REQUIRED] variables only +# Path 2 (GAM Local): Set [REQUIRED] + [GAM-OAUTH] variables +# Path 3 (Production): See docs/deployment.md +# +# Copy this file to .env: cp .env.template .env # Docker Compose automatically reads .env - just run: docker-compose up -d # ============================================ -# REQUIRED - App will not start without these +# [REQUIRED] Essential Configuration # ============================================ -# Gemini API Key (get free key at https://aistudio.google.com/apikey) +# Gemini API Key - Get free key at https://aistudio.google.com/apikey GEMINI_API_KEY= -# Your email address (grants super admin access) +# Super Admin Email - Your email address for admin access SUPER_ADMIN_EMAILS=your-email@example.com # ============================================ -# REQUIRED FOR ADMIN UI LOGIN +# [REQUIRED] Admin UI Authentication # ============================================ -# Create OAuth credentials at: https://console.cloud.google.com/apis/credentials -# Add authorized redirect URI: http://localhost:8001/auth/google/callback +# Test Mode is enabled by default for quick start. +# Switch to Google OAuth for production use. + +# Test Mode (default - for quick testing) +# Enables pre-configured test accounts: test_super_admin@example.com / test123 +# DO NOT USE IN PRODUCTION - set to false and configure OAuth below +ADCP_AUTH_TEST_MODE=true +# Google OAuth (for production use) +# 1. Set ADCP_AUTH_TEST_MODE=false above +# 2. Create OAuth client at: https://console.cloud.google.com/apis/credentials +# 3. Add redirect URI: http://localhost:8001/auth/google/callback +# 4. Enter credentials below: GOOGLE_CLIENT_ID= GOOGLE_CLIENT_SECRET= # ============================================ -# OPTIONAL - Defaults work for local development +# [GAM-OAUTH] Google Ad Manager - Path 2 Only +# ============================================ +# Required ONLY if using GAM adapter with OAuth authentication +# +# IMPORTANT: These are DIFFERENT from Admin UI credentials above! +# 1. Create separate OAuth client at: https://console.cloud.google.com/apis/credentials +# 2. Add redirect URI: http://localhost:8001/tenant/callback/gam +# 3. Enter credentials below: +# 4. Restart services: docker-compose restart +# +# NOTE: Service Account authentication (recommended for production) can be +# configured via Admin UI and does NOT require these OAuth credentials. +# +GAM_OAUTH_CLIENT_ID= +GAM_OAUTH_CLIENT_SECRET= + +# ============================================ +# [OPTIONAL] Port Configuration # ============================================ +# Change these if you have port conflicts -# Ports (change if you have conflicts) POSTGRES_PORT=5432 ADCP_SALES_PORT=8080 ADMIN_UI_PORT=8001 A2A_PORT=8091 -# Google Ad Manager integration (only if using GAM adapter) -# GAM_OAUTH_CLIENT_ID= -# GAM_OAUTH_CLIENT_SECRET= - -# Allowed admin domains (optional - users from these domains get access) -# SUPER_ADMIN_DOMAINS=example.com +# ============================================ +# [OPTIONAL] Advanced Settings +# ============================================ -# Environment: development (strict validation) or production (lenient) +# Environment mode: development (strict validation) or production (lenient) ENVIRONMENT=development + +# Allowed admin domains (optional - grant access to all users from these domains) +# SUPER_ADMIN_DOMAINS=example.com diff --git a/README.md b/README.md index 7edaf8f80..8f7bd6b0c 100644 --- a/README.md +++ b/README.md @@ -11,45 +11,97 @@ The AdCP Sales Agent is a server that: - **Provides an admin interface** for managing inventory and monitoring campaigns - **Handles the full campaign lifecycle** from discovery to reporting -## Quick Start +## Quick Start - Choose Your Setup Path -### Docker Setup (Recommended) +### Path 1: Mock Adapter (5 min) - Recommended First Step + +**Perfect for:** Learning AdCP, testing integrations, development ```bash -# 1. Clone and enter the repository +# 1. Clone and configure git clone https://github.com/adcontextprotocol/salesagent.git cd salesagent - -# 2. Create your .env file cp .env.template .env -# Edit .env with your values (instructions in the file) + +# 2. Set your Gemini API key in .env: +# GEMINI_API_KEY=get-free-at-https://aistudio.google.com/apikey +# (Test mode is enabled by default - no OAuth setup needed) # 3. Start services docker-compose up -d -# 4. Access the Admin UI +# 4. Create test tenant (note the token output!) +docker-compose exec adcp-server python -m scripts.setup.setup_tenant "Test Publisher" \ + --adapter mock \ + --admin-email your-email@example.com + +# 5. Test the MCP server with AdCP CLI (use token from step 4) +uvx adcp http://localhost:8080/mcp/ --auth YOUR_TOKEN list_tools + +# 6. Access Admin UI open http://localhost:8001 +# Login with: test_super_admin@example.com / test123 ``` -**Required configuration in .env:** -- `GEMINI_API_KEY` - Get free at https://aistudio.google.com/apikey -- `SUPER_ADMIN_EMAILS` - Your email address -- `GOOGLE_CLIENT_ID` / `GOOGLE_CLIENT_SECRET` - From [Google Cloud Console](https://console.cloud.google.com/apis/credentials) - - Add redirect URI: `http://localhost:8001/auth/google/callback` +**Using with Claude Desktop:** Add to your Claude config (`~/Library/Application Support/Claude/claude_desktop_config.json`): +```json +{ + "mcpServers": { + "adcp": { + "command": "uvx", + "args": ["mcp-remote", "http://localhost:8080/mcp/", "--header", "x-adcp-auth: YOUR_TOKEN"] + } + } +} +``` -### Creating Your First Tenant +The mock adapter simulates a complete ad server - no external credentials needed. -```bash -# Create a test tenant with mock adapter (no ad server needed) -docker-compose exec adcp-server python -m scripts.setup.setup_tenant "My Publisher" \ - --adapter mock \ - --admin-email your-email@example.com -``` +--- -### Conductor Setup (for parallel workspaces) +### Path 2: Google Ad Manager with OAuth (30 min) -If using Conductor, see [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) for workspace setup. -Each workspace gets unique ports automatically. +**Perfect for:** Quick local testing against a real GAM network + +**Note:** This uses OAuth refresh tokens. For production, use Service Account authentication instead (see Path 3). + +**Prerequisites - complete BEFORE starting:** + +1. **Create GAM OAuth Credentials:** + - Go to [Google Cloud Console](https://console.cloud.google.com/apis/credentials) + - Create OAuth 2.0 Client ID (Web application) + - Add redirect URI: `http://localhost:8001/tenant/callback/gam` + - Save Client ID and Client Secret + +2. **Add to .env file:** + ```bash + GAM_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com + GAM_OAUTH_CLIENT_SECRET=your-client-secret + ``` + +3. **Start services and configure:** + ```bash + docker-compose up -d + docker-compose exec adcp-server python -m scripts.setup.setup_tenant "My Publisher" \ + --adapter google_ad_manager \ + --gam-network-code YOUR_NETWORK_CODE \ + --admin-email your-email@example.com + ``` + +4. **Complete OAuth flow** in Admin UI at http://localhost:8001 + +--- + +### Path 3: Production Deployment + +Use **Service Account** authentication (recommended over OAuth): +- Credentials never expire +- Better security and isolation +- No manual refresh required + +See [docs/deployment.md](docs/deployment.md) for production setup. + +--- ### Troubleshooting @@ -58,8 +110,12 @@ Each workspace gets unique ports automatically. docker-compose logs admin-ui | head -50 # Check for missing env vars ``` +**GAM OAuth error: "Could not determine client ID"?** +- Check that `GAM_OAUTH_CLIENT_ID` and `GAM_OAUTH_CLIENT_SECRET` are set in `.env` +- Run `docker-compose restart` after adding credentials + **OAuth callback 404?** -- Redirect URI must be exactly: `http://localhost:8001/auth/google/callback` +- Redirect URI must match exactly what's in Google Cloud Console **More help:** See [Troubleshooting Guide](docs/TROUBLESHOOTING.md) diff --git a/docs/TROUBLESHOOTING.md b/docs/TROUBLESHOOTING.md index c9aeaf4dc..95ba486e5 100644 --- a/docs/TROUBLESHOOTING.md +++ b/docs/TROUBLESHOOTING.md @@ -267,6 +267,52 @@ See `docker-compose.override.example.yml` for complete configuration. ### GAM Integration Issues +#### "Could not determine client ID from request" + +**Symptom**: Error when trying to save/test GAM configuration with OAuth authentication. + +**Cause**: `GAM_OAUTH_CLIENT_ID` or `GAM_OAUTH_CLIENT_SECRET` environment variables not set. + +**Solution**: + +1. Check if environment variables are set: + ```bash + docker-compose exec adcp-server env | grep GAM_OAUTH + ``` + +2. If missing, add to your `.env` file: + ```bash + GAM_OAUTH_CLIENT_ID=your-client-id.apps.googleusercontent.com + GAM_OAUTH_CLIENT_SECRET=your-client-secret + ``` + +3. Create OAuth credentials at [Google Cloud Console](https://console.cloud.google.com/apis/credentials): + - Create OAuth 2.0 Client ID (Web application) + - Add redirect URI: `http://localhost:8001/tenant/callback/gam` + +4. Restart services: + ```bash + docker-compose restart + ``` + +5. Verify configuration: + ```bash + docker-compose exec adcp-server python scripts/gam_prerequisites_check.py + ``` + +**Alternative**: Use Service Account authentication instead (no OAuth setup required). Configure via Admin UI in the "Service Account Integration" section. + +#### GAM OAuth vs Service Account + +| Feature | OAuth (Refresh Token) | Service Account | +|---------|----------------------|-----------------| +| Setup complexity | Higher (requires OAuth credentials) | Lower (just upload JSON key) | +| Token expiration | Tokens can expire | Never expires | +| Use case | Quick local testing | Production deployments | +| Security | Tied to user account | Isolated service identity | + +**Recommendation**: Use Service Account for production; OAuth for quick testing only. + #### OAuth Token Invalid ```bash # Refresh OAuth token diff --git a/scripts/gam_prerequisites_check.py b/scripts/gam_prerequisites_check.py new file mode 100644 index 000000000..f03b312eb --- /dev/null +++ b/scripts/gam_prerequisites_check.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python3 +""" +Check if GAM prerequisites are configured. + +Usage: + python scripts/gam_prerequisites_check.py + +Returns: + Exit code 0 if all prerequisites met, 1 otherwise +""" +import os +import sys + + +def main(): + """Check GAM prerequisites and print status.""" + print("Checking GAM Prerequisites...\n") + + all_good = True + + # Check OAuth credentials + client_id = os.environ.get("GAM_OAUTH_CLIENT_ID") + client_secret = os.environ.get("GAM_OAUTH_CLIENT_SECRET") + + if client_id: + print(" GAM_OAUTH_CLIENT_ID is set") + else: + print(" GAM_OAUTH_CLIENT_ID is not set") + all_good = False + + if client_secret: + print(" GAM_OAUTH_CLIENT_SECRET is set") + else: + print(" GAM_OAUTH_CLIENT_SECRET is not set") + all_good = False + + # Check service account provisioning capability + gcp_project = os.environ.get("GCP_PROJECT_ID") + if gcp_project: + print(f" GCP_PROJECT_ID is set ({gcp_project})") + print(" Service account auto-provisioning available") + else: + print(" GCP_PROJECT_ID not set") + print(" Service account auto-provisioning unavailable") + print(" Manual service account upload still supported") + + print() + + if not all_good: + print("GAM OAuth prerequisites not fully configured\n") + print("To use GAM with OAuth authentication:") + print(" 1. Go to https://console.cloud.google.com/apis/credentials") + print(" 2. Create OAuth 2.0 Client ID (Web application)") + print(" 3. Add redirect URI: http://localhost:8001/tenant/callback/gam") + print(" 4. Set credentials in .env file:") + print(" GAM_OAUTH_CLIENT_ID=your-client-id") + print(" GAM_OAUTH_CLIENT_SECRET=your-client-secret") + print(" 5. Restart: docker-compose restart\n") + print("Alternative: Use Service Account authentication via Admin UI") + print("(No OAuth setup required)\n") + return 1 + else: + print("All GAM OAuth prerequisites configured!") + print("You can now use OAuth authentication with GAM.\n") + return 0 + + +if __name__ == "__main__": + sys.exit(main()) diff --git a/src/admin/blueprints/tenants.py b/src/admin/blueprints/tenants.py index adc793fd1..fb62fef5c 100644 --- a/src/admin/blueprints/tenants.py +++ b/src/admin/blueprints/tenants.py @@ -215,6 +215,11 @@ def tenant_settings(tenant_id, section=None): if adapter_config_obj and adapter_config_obj.adapter_type == "google_ad_manager": oauth_configured = bool(adapter_config_obj.gam_refresh_token) + # Check if GAM OAuth environment variables are configured + gam_oauth_configured = bool( + os.environ.get("GAM_OAUTH_CLIENT_ID") and os.environ.get("GAM_OAUTH_CLIENT_SECRET") + ) + # Get advertiser data for the advertisers section from src.core.database.models import GAMInventory, Principal @@ -379,6 +384,7 @@ def tenant_settings(tenant_id, section=None): active_adapter=active_adapter, adapter_config=adapter_config_dict, # Use dict format oauth_configured=oauth_configured, + gam_oauth_configured=gam_oauth_configured, # Environment check for GAM OAuth last_sync_time=last_sync_time, running_sync=running_sync, # Pass running sync info principals=principals, diff --git a/templates/tenant_settings.html b/templates/tenant_settings.html index e38608416..b1d807750 100644 --- a/templates/tenant_settings.html +++ b/templates/tenant_settings.html @@ -763,6 +763,28 @@

Triton Digital

Google Ad Manager Configuration

+ {% if not gam_oauth_configured %} + +
+

⚠️ GAM OAuth Not Configured

+

+ OAuth authentication requires environment variables that are not currently set. +

+

To enable OAuth authentication:

+
    +
  1. Create OAuth credentials at Google Cloud Console
  2. +
  3. Add redirect URI: {{ request.url_root }}tenant/callback/gam
  4. +
  5. Add to .env file:
    + GAM_OAUTH_CLIENT_ID=your-client-id
    GAM_OAUTH_CLIENT_SECRET=your-client-secret
    +
  6. +
  7. Restart services: docker-compose restart
  8. +
+

+ Alternative: Use Service Account authentication below - no OAuth setup required. +

+
+ {% endif %} + {% if adapter_config and adapter_config.get('refresh_token') and adapter_config.get('network_code') %}
@@ -862,10 +884,19 @@

✅ Configuration Complete

{% endif %} -
-

🔧 Alternative: Service Account Integration (Recommended for Partners)

+
+

+ 🔧 {% if not gam_oauth_configured %}Recommended: {% endif %}Service Account Integration + {% if not gam_oauth_configured %} + No OAuth Setup Required + {% endif %} +

+ {% if not gam_oauth_configured %} + Service accounts provide secure, non-expiring authentication without OAuth setup. This is the recommended option. + {% else %} For partner integrations, we can create and manage a service account for you. This is more secure and doesn't require sharing credentials. + {% endif %}

{% if adapter_config and adapter_config.get('service_account_email') %}