diff --git a/.env.template b/.env.template index 61b6ed847b6..bf5e8c51ac9 100644 --- a/.env.template +++ b/.env.template @@ -125,7 +125,8 @@ LANGCHAIN_HANDLER=langchain # Main Google Client ID GOOGLE_CLIENT_ID=your_google_client_id GOOGLE_CLIENT_SECRET=your_google_client_secret -GOOGLE_CALLBACK_URL=/api/v1/google-auth/callback +# Note: Google OAuth callback URL is automatically derived from API_HOST +# Callback URL: ${API_HOST}/api/v1/google-auth/callback # Public Google Configuration (for client-side) NEXT_PUBLIC_GOOGLE_DEVELOPER_KEY=your_google_developer_key diff --git a/copilot/copilot.appName.env.template b/copilot/copilot.appName.env.template index 0dbb3ebff6e..2181e025003 100644 --- a/copilot/copilot.appName.env.template +++ b/copilot/copilot.appName.env.template @@ -187,7 +187,8 @@ AAI_DEFAULT_OPENAI_API_KEY= # # ================================================== # # GOOGLE OAUTH & API CONFIGURATION # # ================================================== -# GOOGLE_CALLBACK_URL=/api/v1/google-auth/callback +# # Note: Google OAuth callback URL is automatically derived from API_HOST +# # Callback URL: ${API_HOST}/api/v1/google-auth/callback # GOOGLE_CLIENT_ID= # GOOGLE_CLIENT_SECRET= diff --git a/packages/components/nodes/documentloaders/Gmail/Gmail.ts b/packages/components/nodes/documentloaders/Gmail/Gmail.ts index 4a4286372c8..a656f68c4e1 100644 --- a/packages/components/nodes/documentloaders/Gmail/Gmail.ts +++ b/packages/components/nodes/documentloaders/Gmail/Gmail.ts @@ -120,10 +120,18 @@ class Gmail implements INode { throw new Error('Credentials not found') } + // Validate API_HOST is set for OAuth redirect + if (!process.env.API_HOST) { + throw new Error( + 'API_HOST environment variable is not set. ' + + 'Please set API_HOST in your .env file (e.g., http://localhost:3000)' + ) + } + const credentials = { clientId: process.env.GOOGLE_CLIENT_ID, clientSecret: process.env.GOOGLE_CLIENT_SECRET, - redirectUrl: process.env.GOOGLE_CALLBACK_URL, + redirectUrl: `${process.env.API_HOST}/api/v1/google-auth/callback`, accessToken: credentialData.googleAccessToken, refreshToken: credentialData.googleRefreshToken, expiresAt: credentialData.expiresAt diff --git a/packages/docs/docs/developers/authorization/google-oauth.md b/packages/docs/docs/developers/authorization/google-oauth.md index 5728b9a53b5..f0570e0d018 100644 --- a/packages/docs/docs/developers/authorization/google-oauth.md +++ b/packages/docs/docs/developers/authorization/google-oauth.md @@ -8,6 +8,38 @@ description: Complete guide to setting up Google OAuth for AnswerAgentAI integra Google OAuth 2.0 is required for integrating Google services with AnswerAgentAI, including Gmail, Google Drive, and Google Calendar. This guide covers the complete setup process from creating a Google Cloud Console project to configuring OAuth in your AnswerAgentAI instance. +## Two Types of OAuth Callbacks + +AnswerAgentAI uses two different OAuth callback URLs depending on the use case. **You need to add BOTH to your Google Cloud Console if using both features.** + +### 1. User Authentication Callback + +**URL Pattern:** `${API_HOST}/api/v1/google-auth/callback` + +Used for TheAnswer user authentication via Google. This is automatically derived from your `API_HOST` environment variable. + +| Environment | Callback URL | +|-------------|--------------| +| Local | `http://localhost:3000/api/v1/google-auth/callback` | +| Staging | `https://staging.theanswer.ai/api/v1/google-auth/callback` | +| Production | `https://app.theanswer.ai/api/v1/google-auth/callback` | + +### 2. Flowise Credential Callback + +**URL Pattern:** `${FLOWISE_HOST}/api/v1/oauth2-credential/callback` + +Used for Google Drive, Gmail, and Calendar document loaders/tools within Flowise chatflows. The exact URL is displayed in the credential configuration dialog. + +| Environment | Callback URL | +|-------------|--------------| +| Local | `http://localhost:4000/api/v1/oauth2-credential/callback` | +| Staging | `https://staging.theanswer.ai/api/v1/oauth2-credential/callback` | +| Production | `https://app.theanswer.ai/api/v1/oauth2-credential/callback` | + +:::tip +When configuring Google Cloud Console, add all the callback URLs for the environments you'll be using. Check the "OAUTH REDIRECT URL" field in the Flowise credential dialog to see the exact URL to use. +::: + ## Prerequisites - A Google account @@ -132,13 +164,13 @@ For each API: **For Local Development:** ``` - http://localhost:3000/api/v1/callback/googleoauth + http://localhost:3000/api/v1/google-auth/callback ``` **For Production:** ``` - https://yourdomain.com/api/v1/callback/googleoauth + https://yourdomain.com/api/v1/google-auth/callback ``` 5. **Create and Download** @@ -154,10 +186,12 @@ Add the following environment variables to your AnswerAgentAI instance: # Google OAuth Configuration GOOGLE_CLIENT_ID=your_client_id_here GOOGLE_CLIENT_SECRET=your_client_secret_here -GOOGLE_CALLBACK_URL=http://localhost:3000/api/v1/callback/googleoauth -# For production, use your domain: -# GOOGLE_CALLBACK_URL=https://yourdomain.com/api/v1/callback/googleoauth +# Note: The callback URL is automatically derived from API_HOST +# Make sure API_HOST is set correctly in your environment: +# - Local: API_HOST=http://localhost:3000 +# - Production: API_HOST=https://yourdomain.com +# The callback URL will be: ${API_HOST}/api/v1/google-auth/callback ``` ## Step 7: Credential Configuration in AnswerAgentAI @@ -227,8 +261,14 @@ Once developers have set up the Google OAuth application, end users can connect 1. **"Error 400: redirect_uri_mismatch"** - - Ensure the redirect URI in Google Console matches your AnswerAgentAI instance URL - - Check that GOOGLE_CALLBACK_URL environment variable is correct + - The callback URL is automatically derived from `API_HOST`: + - If `API_HOST=http://localhost:3000`, callback is `http://localhost:3000/api/v1/google-auth/callback` + - If `API_HOST=https://app.example.com`, callback is `https://app.example.com/api/v1/google-auth/callback` + - Add the exact callback URL to Google Cloud Console: + - Go to APIs & Services → Credentials + - Edit your OAuth 2.0 Client ID + - Under "Authorized redirect URIs", add your callback URL + - Ensure the URL matches **exactly** (including http vs https, no trailing slashes) 2. **"Access Blocked: This app's request is invalid"** diff --git a/packages/server/src/config/passport.ts b/packages/server/src/config/passport.ts index 66d60ea9fc9..8fb0a998ee7 100644 --- a/packages/server/src/config/passport.ts +++ b/packages/server/src/config/passport.ts @@ -8,13 +8,24 @@ export default function (passport: any) { // Configure Auth0 SSO strategy for browser login flow configureAuth0Strategy() if (process.env.GOOGLE_CLIENT_ID && process.env.GOOGLE_CLIENT_SECRET) { + // Validate API_HOST is set for Google OAuth callback + if (!process.env.API_HOST) { + console.error( + '❌ ERROR: API_HOST environment variable is not set.\n' + + ' Google OAuth will fail with redirect_uri_mismatch error.\n' + + ' Fix: Add API_HOST to your .env file:\n' + + ' API_HOST=http://localhost:3000 (for local development)\n' + + ' API_HOST=https://yourdomain.com (for production)' + ) + } + passport.use( `google`, new GoogleStrategy( { clientID: process.env.GOOGLE_CLIENT_ID ?? '', clientSecret: process.env.GOOGLE_CLIENT_SECRET ?? '', - callbackURL: process.env.GOOGLE_CALLBACK_URL ?? '', + callbackURL: `${process.env.API_HOST}/api/v1/google-auth/callback`, proxy: true }, async (accessToken, refreshToken, profile, done) => { diff --git a/packages/server/src/utils/constants.ts b/packages/server/src/utils/constants.ts index f2950f0d7b4..5ac9b25377f 100644 --- a/packages/server/src/utils/constants.ts +++ b/packages/server/src/utils/constants.ts @@ -59,7 +59,7 @@ export const WHITELIST_URLS = [ // AAI '/api/v1/google-auth', - process.env.GOOGLE_CALLBACK_URL ?? '/api/v1/google-auth/callback', + '/api/v1/google-auth/callback', '/api/v1/salesforce-auth', '/api/v1/salesforce-auth/callback', '/api/v1/atlassian-auth', diff --git a/packages/server/src/utils/refreshGoogleAccessToken.ts b/packages/server/src/utils/refreshGoogleAccessToken.ts index 96f59987294..1d971f1aab4 100644 --- a/packages/server/src/utils/refreshGoogleAccessToken.ts +++ b/packages/server/src/utils/refreshGoogleAccessToken.ts @@ -12,10 +12,17 @@ export class GoogleOauth2Client { throw new Error('Refresh token is required') } + if (!process.env.API_HOST) { + throw new Error( + 'API_HOST environment variable is not set. ' + + 'Please set API_HOST in your .env file (e.g., http://localhost:3000)' + ) + } + this.oauth2Client = new google.auth.OAuth2( process.env.GOOGLE_CLIENT_ID, process.env.GOOGLE_CLIENT_SECRET, - process.env.GOOGLE_CALLBACK_URL + `${process.env.API_HOST}/api/v1/google-auth/callback` ) this.oauth2Client.setCredentials({