Files
blackroad-os-web/app/api/auth/route.ts
Alexa Amundson 458c2c044b feat: real-time live data integration
- lib/live-data.ts: Shared TypeScript client for blackroad-live-data Worker
- components/live-stats.tsx: LiveStatsBar, RecentRepos, AgentStatusGrid components
- app/page.tsx: Import LiveStatsBar in main page header

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-02-24 14:29:09 -06:00

89 lines
2.9 KiB
TypeScript

import { NextResponse } from 'next/server';
import { createHmac, randomBytes } from 'crypto';
const MASTER_KEY = process.env.BRAT_MASTER_KEY || 'dev-insecure-key-change-in-production';
const INSTANCE_ID = process.env.BRAT_INSTANCE || 'blackroad-os-web';
// Simple credential store — in production, this hits a real DB / Cloudflare KV
const VALID_EMAILS = new Set([
'alexa@blackroad.io',
'lucidia@blackroad.io',
'alice@blackroad.io',
'octavia@blackroad.io',
'aria@blackroad.io',
'shellfish@blackroad.io',
'cipher@blackroad.io',
'cecilia@blackroad.io',
]);
function roleFor(email: string): string {
if (email === 'alexa@blackroad.io') return 'owner';
if (email.endsWith('@blackroad.io')) return 'coordinator';
return 'member';
}
function scopeFor(role: string): string[] {
if (role === 'owner') return ['*'];
if (role === 'coordinator') return ['mesh:*', 'agents:read', 'workers:read', 'api:read'];
return ['mesh:read', 'agents:read'];
}
function mintBRAT(sub: string, role: string): string {
const now = Math.floor(Date.now() / 1000);
const jti = randomBytes(4).toString('hex');
const payload = {
v: 1,
iss: INSTANCE_ID,
sub,
iat: now,
exp: now + 86400 * 30, // 30 days
jti,
role,
scope: scopeFor(role),
};
const encoded = Buffer.from(JSON.stringify(payload)).toString('base64url');
const sig = createHmac('sha256', MASTER_KEY).update(`BRAT_v1.${encoded}`).digest('base64url');
return `BRAT_v1.${encoded}.${sig}`;
}
export async function POST(req: Request) {
try {
const { email, password, name } = await req.json();
if (!email || typeof email !== 'string') {
return NextResponse.json({ error: 'Email required' }, { status: 400 });
}
// In dev/demo mode, accept any @blackroad.io email or allow
// known emails without password check (since we have no real DB yet)
const isBlackRoadEmail = email.endsWith('@blackroad.io');
const isKnownEmail = VALID_EMAILS.has(email.toLowerCase());
// For now: accept any @blackroad.io address OR any email in dev
// TODO: Replace with real credential check against KV / DB
const isDev = process.env.NODE_ENV === 'development' || !process.env.BRAT_MASTER_KEY;
if (!isBlackRoadEmail && !isDev) {
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
}
const role = roleFor(email.toLowerCase());
const token = mintBRAT(email.toLowerCase(), role);
const user = {
id: email.toLowerCase().replace('@', '_').replace('.', '_'),
email: email.toLowerCase(),
name: name || email.split('@')[0].charAt(0).toUpperCase() + email.split('@')[0].slice(1),
workspaceId: 'blackroad-default',
role: role === 'owner' ? 'admin' : 'member' as 'admin' | 'member',
};
return NextResponse.json({ token, user });
} catch (err) {
return NextResponse.json({ error: 'Auth failed' }, { status: 500 });
}
}