aethex-forge/api/auth/foundation-callback.ts
sirpiglr 312072a869 Fix various type errors in API files to ensure proper functionality
Resolves TypeScript errors across multiple API files, including Stripe API version mismatches and incorrect type assertions for request data, enabling successful Vercel builds.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 9203795e-937a-4306-b81d-b4d5c78c240e
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Event-Id: c124cc2e-6c8d-4ca4-80d3-5d34ca7aed66
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/7c94b7a0-29c7-4f2e-94ef-44b2153872b7/9203795e-937a-4306-b81d-b4d5c78c240e/qPXTzuE
Replit-Helium-Checkpoint-Created: true
2025-12-08 01:29:26 +00:00

157 lines
4.9 KiB
TypeScript

/**
* Foundation OAuth Callback Handler
*
* This endpoint receives the authorization code from aethex.foundation after user authentication.
* It exchanges the code for an access token and establishes a session on aethex.dev.
*/
import { getAdminClient } from "../_supabase";
import { VercelRequest, VercelResponse } from "@vercel/node";
const FOUNDATION_URL =
process.env.VITE_FOUNDATION_URL || "https://aethex.foundation";
const API_BASE = process.env.VITE_API_BASE || "https://aethex.dev";
interface FoundationTokenResponse {
access_token: string;
token_type: string;
expires_in: number;
user: {
id: string;
email: string;
username: string;
full_name?: string;
profile_complete: boolean;
};
}
export default async function handler(req: VercelRequest, res: VercelResponse) {
if (req.method !== "GET") {
return res.status(405).json({ error: "Method not allowed" });
}
const { code, state, error } = req.query;
// Handle Foundation errors
if (error) {
const errorDesc = req.query.error_description || error;
return res.redirect(
`/login?error=${error}&message=${encodeURIComponent(String(errorDesc))}`,
);
}
if (!code) {
return res.redirect(
"/login?error=no_code&message=Authorization code not received",
);
}
try {
// Parse state to get redirect destination
let redirectTo = "/dashboard";
if (state) {
try {
const stateData = JSON.parse(decodeURIComponent(String(state)));
redirectTo = stateData.redirectTo || redirectTo;
} catch {
// Ignore state parsing errors, use default
}
}
// Exchange code for token with Foundation
const tokenResponse = await fetch(`${FOUNDATION_URL}/api/oauth/token`, {
method: "POST",
headers: {
"Content-Type": "application/json",
},
body: JSON.stringify({
grant_type: "authorization_code",
code,
client_id: "aethex-corp",
client_secret: process.env.FOUNDATION_OAUTH_CLIENT_SECRET,
redirect_uri: `${API_BASE}/api/auth/foundation-callback`,
}),
});
if (!tokenResponse.ok) {
const errorData = await tokenResponse.json().catch(() => ({}));
console.error("[Foundation OAuth] Token exchange failed:", errorData);
return res.redirect(
`/login?error=token_exchange&message=${encodeURIComponent("Failed to exchange authorization code")}`,
);
}
const tokenData = (await tokenResponse.json()) as FoundationTokenResponse;
if (!tokenData.access_token || !tokenData.user) {
console.error("[Foundation OAuth] Invalid token response");
return res.redirect(
"/login?error=invalid_token&message=Invalid token response",
);
}
// Extract user information from Foundation response
const { user, access_token } = tokenData;
// Ensure user profile exists in aethex.dev's local database
// This syncs the user profile from Foundation
const supabase = getAdminClient();
// Get or create user profile locally
const { data: existingProfile, error: fetchError } = await supabase
.from("user_profiles")
.select("id")
.eq("id", user.id)
.single();
if (fetchError && fetchError.code !== "PGRST116") {
// PGRST116 = no rows found (expected for new users)
console.error(
"[Foundation OAuth] Error fetching user profile:",
fetchError,
);
}
if (!existingProfile) {
// Create user profile from Foundation data
const { error: createError } = await supabase
.from("user_profiles")
.insert({
id: user.id,
email: user.email,
username: user.username || null,
full_name: user.full_name || null,
profile_completed: user.profile_complete || false,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
});
if (createError) {
console.error(
"[Foundation OAuth] Failed to create user profile:",
createError,
);
return res.redirect(
`/login?error=profile_create&message=${encodeURIComponent("Failed to create local user profile")}`,
);
}
}
// Set session cookie with Foundation's access token
// The frontend will use this token for authenticated requests
res.setHeader("Set-Cookie", [
`foundation_access_token=${access_token}; Path=/; HttpOnly; Secure; SameSite=Strict`,
`auth_user_id=${user.id}; Path=/; Secure; SameSite=Strict`,
]);
console.log("[Foundation OAuth] Successfully authenticated user:", user.id);
// Redirect to intended destination or dashboard
return res.redirect(redirectTo);
} catch (error) {
console.error("[Foundation OAuth] Callback error:", error);
return res.redirect(
`/login?error=unknown&message=${encodeURIComponent("An unexpected error occurred")}`,
);
}
}