feat: add auth req and rectify redirect url

This commit is contained in:
Rohit
2025-02-26 13:01:08 +05:30
parent 332266b6d4
commit 84700d3f69

View File

@@ -1,5 +1,4 @@
import { Router, Request, Response } from "express"; import { Router, Request, Response } from "express";
import Airtable from "airtable";
import User from "../models/User"; import User from "../models/User";
import Robot from "../models/Robot"; import Robot from "../models/Robot";
@@ -9,7 +8,7 @@ import { requireSignIn } from "../middlewares/auth";
import { genAPIKey } from "../utils/api"; import { genAPIKey } from "../utils/api";
import { google } from "googleapis"; import { google } from "googleapis";
import { capture } from "../utils/analytics"; import { capture } from "../utils/analytics";
import crypto from 'crypto';
declare module "express-session" { declare module "express-session" {
@@ -584,13 +583,10 @@ router.post(
); );
import crypto from 'crypto';
// Airtable OAuth Routes // Airtable OAuth Routes
router.get("/airtable", (req, res) => { router.get("/airtable", (req: Request, res) => {
const { robotId } = req.query; const authenticatedReq = req as AuthenticatedRequest;
const { robotId } = authenticatedReq.query;
if (!robotId) { if (!robotId) {
return res.status(400).json({ message: "Robot ID is required" }); return res.status(400).json({ message: "Robot ID is required" });
} }
@@ -602,13 +598,12 @@ router.get("/airtable", (req, res) => {
.digest('base64url'); .digest('base64url');
// Store in session // Store in session
req.session.code_verifier = code_verifier; authenticatedReq.session.code_verifier = code_verifier;
req.session.robotId = robotId.toString(); authenticatedReq.session.robotId = robotId.toString();
const params = new URLSearchParams({ const params = new URLSearchParams({
client_id: process.env.AIRTABLE_CLIENT_ID!, client_id: process.env.AIRTABLE_CLIENT_ID!,
redirect_uri: process.env.AIRTABLE_REDIRECT_URI!, redirect_uri: process.env.AIRTABLE_REDIRECT_URI!,
response_type: 'code', response_type: 'code',
state: robotId.toString(), state: robotId.toString(),
scope: 'data.records:read data.records:write schema.bases:read schema.bases:write', scope: 'data.records:read data.records:write schema.bases:read schema.bases:write',
@@ -619,13 +614,16 @@ router.get("/airtable", (req, res) => {
res.redirect(`https://airtable.com/oauth2/v1/authorize?${params}`); res.redirect(`https://airtable.com/oauth2/v1/authorize?${params}`);
}); });
router.get("/airtable/callback", async (req, res) => { router.get("/airtable/callback", async (req: Request, res) => {
const authenticatedReq = req as AuthenticatedRequest;
const baseUrl = process.env.PUBLIC_URL || "http://localhost:5173";
try { try {
const { code, state, error } = req.query; const { code, state, error } = authenticatedReq.query;
if (error) { if (error) {
return res.redirect( return res.redirect(
`${process.env.PUBLIC_URL}/robots/${state}/integrate?error=${encodeURIComponent(error.toString())}` `${baseUrl}/robots/${state}/integrate?error=${encodeURIComponent(error.toString())}`
); );
} }
@@ -634,7 +632,7 @@ router.get("/airtable/callback", async (req, res) => {
} }
// Verify session data // Verify session data
if (!req.session?.code_verifier || req.session.robotId !== state.toString()) { if (!authenticatedReq.session?.code_verifier || authenticatedReq.session.robotId !== state.toString()) {
return res.status(400).json({ return res.status(400).json({
message: "Session expired - please restart the OAuth flow" message: "Session expired - please restart the OAuth flow"
}); });
@@ -649,10 +647,9 @@ router.get("/airtable/callback", async (req, res) => {
body: new URLSearchParams({ body: new URLSearchParams({
grant_type: "authorization_code", grant_type: "authorization_code",
code: code.toString(), code: code.toString(),
client_id: process.env.AIRTABLE_CLIENT_ID!, client_id: process.env.AIRTABLE_CLIENT_ID!,
redirect_uri: process.env.AIRTABLE_REDIRECT_URI!, redirect_uri: process.env.AIRTABLE_REDIRECT_URI!,
code_verifier: req.session.code_verifier code_verifier: authenticatedReq.session.code_verifier
}), }),
}); });
@@ -660,7 +657,7 @@ router.get("/airtable/callback", async (req, res) => {
const errorData = await tokenResponse.json(); const errorData = await tokenResponse.json();
console.error('Token exchange failed:', errorData); console.error('Token exchange failed:', errorData);
return res.redirect( return res.redirect(
`${process.env.PUBLIC_URL}/robots/${state}/integrate?error=${encodeURIComponent(errorData.error_description || 'Authentication failed')}` `${baseUrl}/robots/${state}/integrate?error=${encodeURIComponent(errorData.error_description || 'Authentication failed')}`
); );
} }
@@ -678,40 +675,43 @@ router.get("/airtable/callback", async (req, res) => {
await robot.update({ await robot.update({
airtable_access_token: tokens.access_token, airtable_access_token: tokens.access_token,
airtable_refresh_token: tokens.refresh_token, airtable_refresh_token: tokens.refresh_token,
});
// Clear session data
req.session.destroy((err) => {
if (err) console.error('Session cleanup error:', err);
}); });
res.cookie("airtable_auth_status", "success", { res.cookie("airtable_auth_status", "success", {
httpOnly: false, httpOnly: false,
maxAge: 60000, maxAge: 60000,
}); // 1-minute expiration }); // 1-minute expiration
res.cookie("airtable_auth_message", "Robot successfully authenticated", { // res.cookie("airtable_auth_message", "Robot successfully authenticated", {
// httpOnly: false,
// maxAge: 60000,
// });
res.cookie('robot_auth_robotId', req.session.robotId, {
httpOnly: false, httpOnly: false,
maxAge: 60000, maxAge: 60000,
}); });
res.redirect( // Clear session data
`${process.env.PUBLIC_URL}/robots/${state}/integrate || http://localhost:5173/robots/${state}/integrate` authenticatedReq.session.destroy((err) => {
); if (err) console.error('Session cleanup error:', err);
});
const redirectUrl = `${baseUrl}/robots/`;
res.redirect(redirectUrl);
} catch (error: any) { } catch (error: any) {
console.error('Airtable callback error:', error); console.error('Airtable callback error:', error);
res.redirect( res.redirect(
`${process.env.PUBLIC_URL}/robots/${req.session.robotId}/integrate?error=${encodeURIComponent(error.message)}` `${baseUrl}/robots/${req.session.robotId}/integrate?error=${encodeURIComponent(error.message)}`
); );
} }
}); });
// Get Airtable bases // Get Airtable bases
router.get("/airtable/bases", async (req: AuthenticatedRequest, res) => { router.get("/airtable/bases", async (req: Request, res) => {
const authenticatedReq = req as AuthenticatedRequest;
try { try {
const { robotId } = req.query; const { robotId } = authenticatedReq.query;
if (!robotId) { if (!robotId) {
return res.status(400).json({ message: "Robot ID is required" }); return res.status(400).json({ message: "Robot ID is required" });
} }
@@ -748,7 +748,8 @@ router.get("/airtable/bases", async (req: AuthenticatedRequest, res) => {
}); });
// Update robot with selected base // Update robot with selected base
router.post("/airtable/update", async (req: AuthenticatedRequest, res) => { router.post("/airtable/update", async (req: Request, res) => {
const authenticatedReq = req as AuthenticatedRequest;
const { baseId, robotId , tableName,tableId} = req.body; const { baseId, robotId , tableName,tableId} = req.body;
if (!baseId || !robotId) { if (!baseId || !robotId) {
@@ -772,7 +773,7 @@ router.post("/airtable/update", async (req: AuthenticatedRequest, res) => {
}); });
capture("maxun-oss-airtable-integration-created", { capture("maxun-oss-airtable-integration-created", {
user_id: req.user?.id, user_id: authenticatedReq.user?.id,
robot_id: robotId, robot_id: robotId,
created_at: new Date().toISOString(), created_at: new Date().toISOString(),
}); });
@@ -785,8 +786,9 @@ router.post("/airtable/update", async (req: AuthenticatedRequest, res) => {
}); });
// Remove Airtable integration // Remove Airtable integration
router.post("/airtable/remove", requireSignIn, async (req: AuthenticatedRequest, res) => { router.post("/airtable/remove", requireSignIn, async (req: Request, res) => {
const { robotId } = req.body; const authenticatedReq = req as AuthenticatedRequest;
const { robotId } = authenticatedReq.body;
if (!robotId) { if (!robotId) {
return res.status(400).json({ message: "Robot ID is required" }); return res.status(400).json({ message: "Robot ID is required" });
} }
@@ -810,7 +812,7 @@ router.post("/airtable/remove", requireSignIn, async (req: AuthenticatedRequest,
}); });
capture("maxun-oss-airtable-integration-removed", { capture("maxun-oss-airtable-integration-removed", {
user_id: req.user?.id, user_id: authenticatedReq.user?.id,
robot_id: robotId, robot_id: robotId,
deleted_at: new Date().toISOString(), deleted_at: new Date().toISOString(),
}); });
@@ -825,9 +827,10 @@ router.post("/airtable/remove", requireSignIn, async (req: AuthenticatedRequest,
// Fetch tables from an Airtable base // Fetch tables from an Airtable base
router.get("/airtable/tables", async (req: AuthenticatedRequest, res) => { router.get("/airtable/tables", async (req: Request, res) => {
const authenticatedReq = req as AuthenticatedRequest;
try { try {
const { baseId, robotId } = req.query; const { baseId, robotId } = authenticatedReq.query;
if (!baseId || !robotId) { if (!baseId || !robotId) {
return res.status(400).json({ message: "Base ID and Robot ID are required" }); return res.status(400).json({ message: "Base ID and Robot ID are required" });