widgetdc-cortex / apps /backend /src /routes /socialAuth.ts
Kraft102's picture
Initial deployment - WidgeTDC Cortex Backend v2.1.0
529090e
/**
* Social Media OAuth Routes
* Unified routes for all social platforms
*/
import { Router, Request, Response } from 'express';
import { socialMediaOAuth, SocialPlatform } from '../services/SocialMediaOAuthService.js';
const router = Router();
// Store state parameters (in production, use Redis)
const stateStore = new Map<string, { timestamp: number; platform: SocialPlatform; returnUrl?: string }>();
/**
* GET /api/auth/:platform/login
* Initiate OAuth flow for any platform
*/
router.get('/:platform/login', (req: Request, res: Response) => {
try {
const platform = req.params.platform as SocialPlatform;
const returnUrl = req.query.returnUrl as string;
if (!Object.values(SocialPlatform).includes(platform)) {
return res.status(400).json({
success: false,
error: `Invalid platform: ${platform}`,
supportedPlatforms: Object.values(SocialPlatform),
});
}
const state = Math.random().toString(36).substring(7);
stateStore.set(state, {
timestamp: Date.now(),
platform,
returnUrl,
});
// Clean old states
for (const [key, value] of stateStore.entries()) {
if (Date.now() - value.timestamp > 300000) {
stateStore.delete(key);
}
}
const authUrl = socialMediaOAuth.getAuthorizationUrl(platform, state);
res.json({
success: true,
authUrl,
platform,
message: `Redirect user to authUrl to authorize ${platform}`,
});
} catch (error: any) {
res.status(500).json({
success: false,
error: error.message,
});
}
});
/**
* GET /api/auth/:platform/callback
* OAuth callback for any platform
*/
router.get('/:platform/callback', async (req: Request, res: Response) => {
try {
const platform = req.params.platform as SocialPlatform;
const { code, state, error, error_description } = req.query;
// Handle OAuth errors
if (error) {
return res.status(400).send(`
<html>
<body style="font-family: system-ui; padding: 40px; text-align: center;">
<h1>❌ ${platform} Authorization Failed</h1>
<p><strong>Error:</strong> ${error}</p>
<p><strong>Description:</strong> ${error_description || 'Unknown error'}</p>
<p><a href="http://localhost:8888/dashboard">Return to Dashboard</a></p>
</body>
</html>
`);
}
// Verify state
const stateData = stateStore.get(state as string);
if (!stateData || stateData.platform !== platform) {
return res.status(400).send(`
<html>
<body style="font-family: system-ui; padding: 40px; text-align: center;">
<h1>❌ Invalid State Parameter</h1>
<p>State verification failed. Please try again.</p>
<p><a href="http://localhost:8888/dashboard">Return to Dashboard</a></p>
</body>
</html>
`);
}
stateStore.delete(state as string);
// Exchange code for token
const { accessToken, userId } = await socialMediaOAuth.exchangeCodeForToken(platform, code as string);
// Success
const returnUrl = stateData.returnUrl || 'http://localhost:8888/dashboard';
res.send(`
<html>
<head>
<style>
body {
font-family: system-ui;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
display: flex;
align-items: center;
justify-content: center;
height: 100vh;
margin: 0;
color: white;
}
.container {
background: rgba(255, 255, 255, 0.1);
backdrop-filter: blur(10px);
border-radius: 20px;
padding: 40px;
text-align: center;
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
}
h1 { margin-top: 0; }
.button {
display: inline-block;
background: white;
color: #667eea;
padding: 12px 24px;
border-radius: 8px;
text-decoration: none;
font-weight: 600;
margin-top: 20px;
}
</style>
</head>
<body>
<div class="container">
<h1>✅ ${platform.toUpperCase()} Connected</h1>
<p><strong>User ID:</strong> ${userId}</p>
<p>You can now ingest ${platform} content into WidgeTDC.</p>
<a href="${returnUrl}" class="button">Return to Dashboard</a>
</div>
<script>
if (window.opener) {
window.opener.postMessage({
type: 'social-oauth-success',
platform: '${platform}',
userId: '${userId}'
}, '*');
setTimeout(() => window.close(), 2000);
}
</script>
</body>
</html>
`);
} catch (error: any) {
console.error('❌ OAuth callback error:', error);
res.status(500).send(`
<html>
<body style="font-family: system-ui; padding: 40px; text-align: center;">
<h1>❌ Authentication Error</h1>
<p>${error.message}</p>
<p><a href="http://localhost:8888/dashboard">Return to Dashboard</a></p>
</body>
</html>
`);
}
});
/**
* GET /api/social/:platform/status
* Check authentication status
*/
router.get('/:platform/status', (req: Request, res: Response) => {
const platform = req.params.platform as SocialPlatform;
const userId = req.query.userId as string;
if (!userId) {
return res.json({ authenticated: false });
}
const isAuth = socialMediaOAuth.isAuthenticated(platform, userId);
res.json({
authenticated: isAuth,
platform,
userId: isAuth ? userId : null,
});
});
/**
* POST /api/social/:platform/ingest
* Fetch and ingest posts (requires approval)
*/
router.post('/:platform/ingest', async (req: Request, res: Response) => {
try {
const platform = req.params.platform as SocialPlatform;
const { userId, limit = 10 } = req.body;
if (!userId) {
return res.status(400).json({
success: false,
error: 'userId required in request body',
});
}
if (!socialMediaOAuth.isAuthenticated(platform, userId)) {
return res.status(401).json({
success: false,
error: 'Not authenticated. Please login first.',
loginUrl: `/api/auth/${platform}/login`,
});
}
const { posts, approvalRequestId } = await socialMediaOAuth.fetchAndIngestPosts(
platform,
userId,
limit
);
res.json({
success: true,
platform,
postCount: posts.length,
approvalRequestId,
message: `${posts.length} posts from ${platform} ingested after approval`,
});
} catch (error: any) {
res.status(500).json({
success: false,
error: error.message,
});
}
});
/**
* DELETE /api/social/:platform/logout
* Revoke access token
*/
router.delete('/:platform/logout', (req: Request, res: Response) => {
const platform = req.params.platform as SocialPlatform;
const { userId } = req.body;
if (!userId) {
return res.status(400).json({
success: false,
error: 'userId required',
});
}
socialMediaOAuth.revokeAccess(platform, userId);
res.json({
success: true,
platform,
message: `${platform} access revoked`,
});
});
export default router;