Spaces:
Running
Running
File size: 3,916 Bytes
cf87afd bed4e30 adb3b9e bed4e30 cf87afd bed4e30 2b3949f cf87afd bed4e30 cf87afd bed4e30 cf87afd bed4e30 cf87afd bed4e30 cf87afd bed4e30 cf87afd bed4e30 cf87afd bed4e30 cf87afd bed4e30 cf87afd bed4e30 cf87afd 6ce04ff cf87afd 87d6c49 cf87afd 87d6c49 cf87afd bed4e30 d798d35 cf87afd d798d35 cf87afd d798d35 cf87afd bed4e30 cf87afd bed4e30 cf87afd 6ce04ff bed4e30 adb3b9e | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 | import express from 'express';
import { createClient } from '@supabase/supabase-js';
import jwt from 'jsonwebtoken';
import { v4 as uuidv4 } from 'uuid';
import cors from 'cors';
const app = express();
const supabase = createClient(process.env.SUPABASE_URL,
process.env.SUPABASE_SERVICE_ROLE_KEY);
app.use(cors());
app.use(express.json());
app.get('/', (req, res) => res.send('Gateway Active'));
const tempKeys = new Map(); // key -> { uid, projectId, createdAt }
// --- MIDDLEWARE: AUTHENTICATION ---
const verifySupabaseSession = async (req, res, next) => {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith('Bearer ')) return res.status(401).json({ error: 'Unauthorized' });
const idToken = authHeader.split(' ')[1];
try {
const { data: { user }, error } = await supabase.auth.getUser(idToken);
if (error || !user) throw new Error("Invalid Session");
req.user = user;
next();
} catch (err) {
return res.status(403).json({ error: 'Session Expired', details: err.message });
}
};
// 1. GENERATE TEMP KEY (Called by Auth Page)
app.post('/key', verifySupabaseSession, (req, res) => {
const { projectId } = req.body;
const key = `key_${uuidv4().replace(/-/g, '')}`;
// Store key with 5-minute expiry
tempKeys.set(key, {
uid: req.user.id,
projectId: projectId || 'default',
createdAt: Date.now()
});
res.json({ key, expiresIn: 300 });
});
// 2. REDEEM KEY (Called by Local CLI)
app.post('/redeem', async (req, res) => {
const { key, deviceName } = req.body;
if (!tempKeys.has(key)) return res.status(404).json({ error: 'Key invalid' });
const data = tempKeys.get(key);
if (Date.now() - data.createdAt > 300000) {
tempKeys.delete(key);
return res.status(410).json({ error: 'Key expired' });
}
const sessionSecret = uuidv4();
// Create persistent session in DB
const { data: session, error } = await supabase
.from('user_sessions')
.insert({
user_id: data.uid,
project_id: data.projectId,
session_secret: sessionSecret,
device_name: deviceName || 'Unknown Device'
})
.select()
.single();
if (error) return res.status(500).json({ error: "Failed to create session" });
// Sign JWT using the unique session secret
// Payload contains the session ID so Gateway can verify DB existence
const token = jwt.sign(
{ uid: data.uid, projectId: data.projectId, sid: session.id },
sessionSecret,
{ expiresIn: '30d' }
);
tempKeys.delete(key);
res.json({ token });
});
// 3. LIST ACTIVE SESSIONS (Called by Web Console)
app.get('/sessions', verifySupabaseSession, async (req, res) => {
const { data, error } = await supabase
.from('user_sessions')
.select('id, project_id, device_name, created_at, last_used_at')
.eq('user_id', req.user.id);
if (error) return res.status(500).json({ error: error.message });
res.json(data);
});
// 4. REVOKE SESSION (Called by Web Console)
app.post('/revoke', verifySupabaseSession, async (req, res) => {
const { sessionId } = req.body;
if (!sessionId) return res.status(400).json({ error: "Session ID required" });
// RLS ensures the user can only delete their own,
// but we add an explicit check for safety.
const { error } = await supabase
.from('user_sessions')
.delete()
.eq('id', sessionId)
.eq('user_id', req.user.id);
if (error) return res.status(500).json({ error: "Revocation failed" });
console.log(`🗑️ Session ${sessionId} revoked by user ${req.user.id}`);
res.json({ success: true });
});
app.listen(7860, () => console.log("🚀 Auth Proxy: Secure & Revocable")); |