-
Notifications
You must be signed in to change notification settings - Fork 0
feat: enhance Google OAuth integration and improve environment variab… #57
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -3,6 +3,28 @@ import { GoogleOAuthService } from "@repo/nodes"; | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import type { OAuthTokens } from "@repo/nodes"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { AuthRequest, userMiddleware } from "./userRoutes/userMiddleware.js"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { Router, Request, Response } from "express"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import dotenv from "dotenv"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { fileURLToPath } from "url"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import { dirname, join } from "path"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Get the directory of this file | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const __filename = fileURLToPath(import.meta.url); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const __dirname = dirname(__filename); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Load environment variables from the http-backend directory | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // When running from dist/, we need to go up to the src directory, then to the root | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Use override: true to ensure this .env file takes precedence over others | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const envPath = join(__dirname, "../../.env"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| dotenv.config({ path: envPath, override: true }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Log OAuth configuration at module load | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const REDIRECT_URI = process.env.GOOGLE_REDIRECT_URI || "http://localhost:3002/oauth/google/callback"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log("🔐 OAuth Configuration loaded:"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(" .env path:", envPath); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(" GOOGLE_REDIRECT_URI from env:", process.env.GOOGLE_REDIRECT_URI || "NOT SET (using default)"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(" Using Redirect URI:", REDIRECT_URI); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(" GOOGLE_CLIENT_ID:", process.env.GOOGLE_CLIENT_ID ? `${process.env.GOOGLE_CLIENT_ID.substring(0, 20)}...` : "NOT SET"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log(" GOOGLE_CLIENT_SECRET:", process.env.GOOGLE_CLIENT_SECRET ? "SET" : "NOT SET"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const googleAuth: Router = Router(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| googleAuth.get( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -11,12 +33,22 @@ googleAuth.get( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| async (req: AuthRequest, res: Response) => { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // const userId = req.user?.id || "test_user"; // Get from auth middleware | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const userId = req.user?.id; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const workflowId = req.query.workflowId as string | undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Ensure redirect URI matches Google Cloud Console configuration | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const redirectUri = REDIRECT_URI; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log("🔐 OAuth Initiate - Redirect URI:", redirectUri); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log("🔐 OAuth Initiate - User ID:", userId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log("🔐 OAuth Initiate - Workflow ID:", workflowId || "NOT PROVIDED"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Encode userId and workflowId in state (format: userId|workflowId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const state = workflowId ? `${userId}|${workflowId}` : userId; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const oauth2 = new google.auth.OAuth2( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process.env.GOOGLE_CLIENT_ID, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process.env.GOOGLE_CLIENT_SECRET, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| process.env.GOOGLE_REDIRECT_URI || | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "http://localhost:3002/auth/google/callback" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| redirectUri | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const authUrl = oauth2.generateAuthUrl({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -27,10 +59,11 @@ googleAuth.get( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "https://www.googleapis.com/auth/gmail.send", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "https://www.googleapis.com/auth/gmail.readonly", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ], | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state: userId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| state: state, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| prompt: "consent", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| console.log("🔐 OAuth Initiate - Generated Auth URL:", authUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.redirect(authUrl); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -46,11 +79,29 @@ googleAuth.get( | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return res.json({ error: "Missing or invalid authorization code" }); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Ensure redirect URI matches Google Cloud Console configuration | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const redirectUri = REDIRECT_URI; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Parse state: format is "userId" or "userId|workflowId" | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let userId: string | undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| let workflowId: string | undefined; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (state && typeof state === "string") { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| const parts = state.split("|"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| userId = parts[0]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| workflowId = parts[1]; // Will be undefined if not provided | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+88
to
+94
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (state && typeof state === "string") { | |
| const parts = state.split("|"); | |
| userId = parts[0]; | |
| workflowId = parts[1]; // Will be undefined if not provided | |
| } | |
| // Validate state parameter | |
| if (!state || typeof state !== "string") { | |
| console.error("🔐 OAuth Callback - Missing or invalid state parameter:", state); | |
| return res.status(400).json({ error: "Missing or invalid state parameter" }); | |
| } | |
| const parts = state.split("|"); | |
| userId = parts[0] || undefined; | |
| workflowId = parts[1]; // Will be undefined if not provided | |
| if (!userId) { | |
| console.error("🔐 OAuth Callback - Missing userId in state parameter:", state); | |
| return res.status(400).json({ error: "Missing user identifier in state parameter" }); | |
| } |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid automated semicolon insertion (97% of all statements in the enclosing function have an explicit semicolon).
| await Oauth.saveCredentials(userId, tokens as OAuthTokens) | |
| await Oauth.saveCredentials(userId, tokens as OAuthTokens); |
Copilot
AI
Jan 16, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The variable workflowId is re-declared in the catch block even though it was already declared in the outer scope at line 87. This shadowing is unnecessary and can be confusing. Remove the duplicate declaration and use the existing variable.
| // Parse state to get workflowId for error redirect | |
| let workflowId: string | undefined; | |
| if (state && typeof state === "string") { | |
| const parts = state.split("|"); | |
| workflowId = parts[1]; | |
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hardcoded frontend URLs and variable shadowing.
Two issues:
-
Hardcoded URLs:
http://localhost:3000won't work in production. Use an environment variable likeFRONTEND_URL. -
Variable shadowing:
workflowIdis re-declared on line 139 but was already declared on line 87 and is in scope. This causes the outer variable to be shadowed and could lead to confusion.
Suggested fix
+const FRONTEND_URL = process.env.FRONTEND_URL || "http://localhost:3000";
+
// Redirect to workflow page if workflowId is provided, otherwise to general workflow page
const redirectUrl = workflowId
- ? `http://localhost:3000/workflows/${workflowId}`
- : "http://localhost:3000/workflow";
+ ? `${FRONTEND_URL}/workflows/${workflowId}`
+ : `${FRONTEND_URL}/workflow`;
console.log(' Redirecting to:', redirectUrl);
return res.redirect(redirectUrl);
} catch (err: any) {
console.error("Google token exchange error:", err);
- // Parse state to get workflowId for error redirect
- let workflowId: string | undefined;
- if (state && typeof state === "string") {
- const parts = state.split("|");
- workflowId = parts[1];
- }
+ // workflowId is already parsed above, no need to re-parse
const errorUrl = workflowId
- ? `http://localhost:3000/workflows/${workflowId}?google=error&msg=${encodeURIComponent(err?.message ?? "Token exchange failed")}`
- : `http://localhost:3000/workflow?google=error&msg=${encodeURIComponent(err?.message ?? "Token exchange failed")}`;
+ ? `${FRONTEND_URL}/workflows/${workflowId}?google=error&msg=${encodeURIComponent(err?.message ?? "Token exchange failed")}`
+ : `${FRONTEND_URL}/workflow?google=error&msg=${encodeURIComponent(err?.message ?? "Token exchange failed")}`;
return res.redirect(errorUrl);
}🤖 Prompt for AI Agents
In `@apps/http-backend/src/routes/google_callback.ts` around lines 131 - 147,
Replace the hardcoded frontend base URL with an environment-driven value (e.g.
process.env.FRONTEND_URL) when building redirectUrl and errorUrl in
google_callback.ts so production deployments use the correct host; also stop
redeclaring workflowId inside the catch block (remove the inner "let workflowId"
and assign to the existing workflowId variable parsed from state) so you don't
shadow the outer variable—use the existing workflowId plus
encodeURIComponent(err?.message ?? "Token exchange failed") when constructing
the errorUrl.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Debug endpoint exposes sensitive OAuth configuration without authentication.
The /debug/config endpoint is not protected by userMiddleware and exposes OAuth client IDs and configuration details. This is a security risk in production.
Suggested fix - protect or disable in production
// Debug endpoint to check OAuth configuration
-googleAuth.get('/debug/config', async(req: Request, res: Response)=>{
+googleAuth.get('/debug/config', userMiddleware, async(req: AuthRequest, res: Response)=>{
+ // Only allow in development
+ if (process.env.NODE_ENV === 'production') {
+ return res.status(403).json({ error: 'Debug endpoints disabled in production' });
+ }
try {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Debug endpoint to check OAuth configuration | |
| googleAuth.get('/debug/config', async(req: Request, res: Response)=>{ | |
| try { | |
| const redirectUri = REDIRECT_URI; | |
| const clientId = process.env.GOOGLE_CLIENT_ID; | |
| const hasClientSecret = !!process.env.GOOGLE_CLIENT_SECRET; | |
| // Create a test OAuth2 client to verify configuration | |
| const oauth2 = new google.auth.OAuth2( | |
| clientId, | |
| process.env.GOOGLE_CLIENT_SECRET, | |
| redirectUri | |
| ); | |
| const testAuthUrl = oauth2.generateAuthUrl({ | |
| access_type: "offline", | |
| scope: ["https://www.googleapis.com/auth/spreadsheets"], | |
| state: "test", | |
| prompt: "consent", | |
| }); | |
| const urlObj = new URL(testAuthUrl); | |
| const redirectUriInUrl = urlObj.searchParams.get('redirect_uri'); | |
| return res.json({ | |
| environment: { | |
| GOOGLE_REDIRECT_URI: redirectUri, | |
| GOOGLE_CLIENT_ID: clientId ? `${clientId.substring(0, 20)}...` : "NOT SET", | |
| GOOGLE_CLIENT_SECRET: hasClientSecret ? "SET" : "NOT SET", | |
| }, | |
| oauth2Client: { | |
| redirectUri: redirectUri, | |
| redirectUriInGeneratedUrl: redirectUriInUrl, | |
| matches: redirectUri === redirectUriInUrl, | |
| }, | |
| testAuthUrl: testAuthUrl, | |
| message: redirectUri === redirectUriInUrl | |
| ? "✅ Configuration looks correct!" | |
| : "❌ Redirect URI mismatch detected!" | |
| }); | |
| } catch (err) { | |
| return res.status(500).json({ | |
| error: err instanceof Error ? err.message : 'Unknown error', | |
| stack: err instanceof Error ? err.stack : undefined | |
| }); | |
| } | |
| }); | |
| // Debug endpoint to check OAuth configuration | |
| googleAuth.get('/debug/config', userMiddleware, async(req: AuthRequest, res: Response)=>{ | |
| // Only allow in development | |
| if (process.env.NODE_ENV === 'production') { | |
| return res.status(403).json({ error: 'Debug endpoints disabled in production' }); | |
| } | |
| try { | |
| const redirectUri = REDIRECT_URI; | |
| const clientId = process.env.GOOGLE_CLIENT_ID; | |
| const hasClientSecret = !!process.env.GOOGLE_CLIENT_SECRET; | |
| // Create a test OAuth2 client to verify configuration | |
| const oauth2 = new google.auth.OAuth2( | |
| clientId, | |
| process.env.GOOGLE_CLIENT_SECRET, | |
| redirectUri | |
| ); | |
| const testAuthUrl = oauth2.generateAuthUrl({ | |
| access_type: "offline", | |
| scope: ["https://www.googleapis.com/auth/spreadsheets"], | |
| state: "test", | |
| prompt: "consent", | |
| }); | |
| const urlObj = new URL(testAuthUrl); | |
| const redirectUriInUrl = urlObj.searchParams.get('redirect_uri'); | |
| return res.json({ | |
| environment: { | |
| GOOGLE_REDIRECT_URI: redirectUri, | |
| GOOGLE_CLIENT_ID: clientId ? `${clientId.substring(0, 20)}...` : "NOT SET", | |
| GOOGLE_CLIENT_SECRET: hasClientSecret ? "SET" : "NOT SET", | |
| }, | |
| oauth2Client: { | |
| redirectUri: redirectUri, | |
| redirectUriInGeneratedUrl: redirectUriInUrl, | |
| matches: redirectUri === redirectUriInUrl, | |
| }, | |
| testAuthUrl: testAuthUrl, | |
| message: redirectUri === redirectUriInUrl | |
| ? "✅ Configuration looks correct!" | |
| : "❌ Redirect URI mismatch detected!" | |
| }); | |
| } catch (err) { | |
| return res.status(500).json({ | |
| error: err instanceof Error ? err.message : 'Unknown error', | |
| stack: err instanceof Error ? err.stack : undefined | |
| }); | |
| } | |
| }); |
🤖 Prompt for AI Agents
In `@apps/http-backend/src/routes/google_callback.ts` around lines 151 - 197, The
/debug/config route (googleAuth.get('/debug/config')) currently returns
sensitive OAuth info (REDIRECT_URI, GOOGLE_CLIENT_ID/SECRET, testAuthUrl)
without authentication; fix by either removing the endpoint from production or
protecting it: wrap the handler with the existing userMiddleware (or another
auth middleware) so only authenticated users can access it, or gate the entire
route behind an environment check (e.g., only register/return when NODE_ENV ===
'development' or a DEBUG_CONFIG flag) and ensure GOOGLE_CLIENT_SECRET is never
returned; update the route registration accordingly.
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -213,20 +213,33 @@ router.get( | |||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // if (credentials.length === 0) { | ||||||||||||||||||||||||||||||||
| // // No credentials found - return the correct auth URL | ||||||||||||||||||||||||||||||||
| // const authUrl = `${process.env.BACKEND_URL || "http://localhost:3002"}/oauth/google/initiate`; | ||||||||||||||||||||||||||||||||
| // return res.status(statusCodes.OK).json({ | ||||||||||||||||||||||||||||||||
| // message: | ||||||||||||||||||||||||||||||||
| // "Credentials not found create credentials using this auth url", | ||||||||||||||||||||||||||||||||
| // Data: authUrl, | ||||||||||||||||||||||||||||||||
| // }); | ||||||||||||||||||||||||||||||||
| // } | ||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||
| // // Credentials found - return them | ||||||||||||||||||||||||||||||||
| // return res.status(statusCodes.OK).json({ | ||||||||||||||||||||||||||||||||
| // message: "Credentials Fetched successfully", | ||||||||||||||||||||||||||||||||
| // Data: credentials, | ||||||||||||||||||||||||||||||||
| // }); | ||||||||||||||||||||||||||||||||
|
Comment on lines
+216
to
+230
|
||||||||||||||||||||||||||||||||
| // if (credentials.length === 0) { | |
| // // No credentials found - return the correct auth URL | |
| // const authUrl = `${process.env.BACKEND_URL || "http://localhost:3002"}/oauth/google/initiate`; | |
| // return res.status(statusCodes.OK).json({ | |
| // message: | |
| // "Credentials not found create credentials using this auth url", | |
| // Data: authUrl, | |
| // }); | |
| // } | |
| // // Credentials found - return them | |
| // return res.status(statusCodes.OK).json({ | |
| // message: "Credentials Fetched successfully", | |
| // Data: credentials, | |
| // }); |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -1,34 +1,53 @@ | ||||||||||||||||||||||
| "use client"; | ||||||||||||||||||||||
| import { useEffect, useState } from "react"; | ||||||||||||||||||||||
| import { getCredentials } from "../workflow/lib/config"; | ||||||||||||||||||||||
| import { BACKEND_URL } from "@repo/common/zod"; | ||||||||||||||||||||||
|
|
||||||||||||||||||||||
| export const useCredentials = (type: string, workflowId?: string): any => { | ||||||||||||||||||||||
|
||||||||||||||||||||||
| export const useCredentials = (type: string, workflowId?: string): any => { | |
| interface UseCredentialsResult { | |
| cred: any[]; | |
| authUrl: string | null; | |
| } | |
| export const useCredentials = ( | |
| type: string, | |
| workflowId?: string | |
| ): UseCredentialsResult => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# First, find and examine the getCredentials function implementation
echo "=== Searching for getCredentials function ==="
find . -name "config.ts" -path "*/workflow/lib/*" | head -5Repository: Dev-Pross/BuildFlow
Length of output: 146
🏁 Script executed:
#!/bin/bash
# Look at the useCredential.ts file to understand the context
find . -name "useCredential.ts" | head -5Repository: Dev-Pross/BuildFlow
Length of output: 100
🏁 Script executed:
#!/bin/bash
# Search for getCredentials definition and usage
rg "const getCredentials|export.*getCredentials" -t ts --type-add 'ts:*.ts' -B 2 -A 10Repository: Dev-Pross/BuildFlow
Length of output: 799
🏁 Script executed:
#!/bin/bash
# Get more context from config.ts to see the complete getCredentials function
cat -n apps/web/app/workflow/lib/config.ts | grep -A 20 "export const getCredentials"Repository: Dev-Pross/BuildFlow
Length of output: 691
🏁 Script executed:
#!/bin/bash
# Read the complete useCredential.ts file
cat -n apps/web/app/hooks/useCredential.tsRepository: Dev-Pross/BuildFlow
Length of output: 1948
🏁 Script executed:
#!/bin/bash
# Search for backend endpoint definition or API route
find . -type f \( -name "*.ts" -o -name "*.js" -o -name "*.py" \) -path "*/backend/*" -o -path "*/server/*" -o -path "*/api/*" | head -20Repository: Dev-Pross/BuildFlow
Length of output: 272
🏁 Script executed:
#!/bin/bash
# Search for mentions of getCredentials endpoint in the codebase
rg "getCredentials|user/getCredentials" -t ts -t js -t py -B 2 -A 5 | head -100Repository: Dev-Pross/BuildFlow
Length of output: 6388
🏁 Script executed:
#!/bin/bash
# Look for type definitions or interfaces related to credentials response
rg "data.*Data|hasCredentials" -t ts -B 2 -A 2Repository: Dev-Pross/BuildFlow
Length of output: 5701
Fix case mismatch in getCredentials response handling.
The backend endpoint returns { data, hasCredentials } with lowercase data, but getCredentials in apps/web/app/workflow/lib/config.ts accesses response.data.Data (uppercase). This causes the function to return undefined, making the Array.isArray(response) check in useCredential.ts fail and silently set credentials to an empty array.
Change line 40 in apps/web/app/workflow/lib/config.ts from:
return response.data.Data;
to:
return response.data.data;
🤖 Prompt for AI Agents
In `@apps/web/app/hooks/useCredential.ts` around lines 20 - 27, The getCredentials
function is returning the wrong property due to a case mismatch causing
useCredential's Array.isArray check to fail; in the getCredentials
implementation (the function named getCredentials in workflow/lib/config.ts)
change the return to use the lowercase nested field (return response.data.data)
instead of response.data.Data so that getCredentials returns the actual array
the useCredential hook expects and setCred receives the correct value.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If
userIdis undefined (which can happen if authentication middleware fails), the state will be 'undefined' as a string. This creates an invalid state parameter that will fail to parse properly in the callback. Add validation to ensure userId exists before proceeding with OAuth initiation.