163 lines
4.9 KiB
TypeScript
163 lines
4.9 KiB
TypeScript
/**
|
|
* Next.js API Route for Realtime Voice Agent
|
|
*
|
|
* File: app/api/voice/session/route.ts
|
|
*
|
|
* Demonstrates:
|
|
* - Generating ephemeral API keys for voice sessions
|
|
* - Securing realtime agent access
|
|
* - NEVER exposing main API key to clients
|
|
*
|
|
* CRITICAL: Never send your main OPENAI_API_KEY to the browser!
|
|
* Use ephemeral session keys with short expiration.
|
|
*/
|
|
|
|
import { NextRequest, NextResponse } from 'next/server';
|
|
import OpenAI from 'openai';
|
|
|
|
// ========================================
|
|
// POST /api/voice/session
|
|
// Generate ephemeral API key for voice session
|
|
// ========================================
|
|
|
|
export async function POST(request: NextRequest) {
|
|
try {
|
|
// Optional: Authenticate user first
|
|
// const session = await getServerSession(authOptions);
|
|
// if (!session) {
|
|
// return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
// }
|
|
|
|
// Create OpenAI client with main API key (server-side only)
|
|
const openai = new OpenAI({
|
|
apiKey: process.env.OPENAI_API_KEY,
|
|
});
|
|
|
|
// Generate ephemeral key
|
|
// NOTE: As of 2025-10-26, OpenAI doesn't have a dedicated ephemeral key endpoint
|
|
// You may need to use session tokens or implement your own proxy
|
|
//
|
|
// Recommended approach: Create a proxy that validates requests and
|
|
// forwards to OpenAI API with your key server-side
|
|
|
|
// For demonstration, we'll show the pattern:
|
|
// In production, implement a secure proxy or use OpenAI's ephemeral keys when available
|
|
|
|
// Option 1: Return a session token (your own implementation)
|
|
const sessionToken = generateSecureSessionToken();
|
|
|
|
// Store session token mapping to your API key in Redis/KV
|
|
// await redis.set(`session:${sessionToken}`, process.env.OPENAI_API_KEY, {
|
|
// ex: 3600, // 1 hour expiration
|
|
// });
|
|
|
|
return NextResponse.json({
|
|
sessionToken,
|
|
expiresIn: 3600, // seconds
|
|
});
|
|
|
|
// Option 2: If OpenAI provides ephemeral keys API (future)
|
|
// const ephemeralKey = await openai.ephemeralKeys.create({
|
|
// expiresIn: 3600,
|
|
// });
|
|
// return NextResponse.json({
|
|
// apiKey: ephemeralKey.key,
|
|
// expiresIn: ephemeralKey.expiresIn,
|
|
// });
|
|
|
|
} catch (error: any) {
|
|
console.error('Session creation error:', error);
|
|
|
|
return NextResponse.json(
|
|
{ error: 'Failed to create session' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// Helper: Generate Secure Session Token
|
|
// ========================================
|
|
|
|
function generateSecureSessionToken(): string {
|
|
// Generate cryptographically secure random token
|
|
const array = new Uint8Array(32);
|
|
crypto.getRandomValues(array);
|
|
return Array.from(array, byte => byte.toString(16).padStart(2, '0')).join('');
|
|
}
|
|
|
|
// ========================================
|
|
// Proxy Endpoint (Recommended Pattern)
|
|
// File: app/api/voice/proxy/route.ts
|
|
// ========================================
|
|
|
|
/**
|
|
* This proxy validates session tokens and forwards requests to OpenAI API
|
|
* This is the RECOMMENDED approach to avoid exposing your API key
|
|
*/
|
|
|
|
export async function POST_PROXY(request: NextRequest) {
|
|
try {
|
|
// Get session token from request
|
|
const authHeader = request.headers.get('authorization');
|
|
const sessionToken = authHeader?.replace('Bearer ', '');
|
|
|
|
if (!sessionToken) {
|
|
return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
|
|
}
|
|
|
|
// Validate session token
|
|
// const apiKey = await redis.get(`session:${sessionToken}`);
|
|
// if (!apiKey) {
|
|
// return NextResponse.json({ error: 'Invalid session' }, { status: 401 });
|
|
// }
|
|
|
|
// Get the actual OpenAI API request from client
|
|
const body = await request.json();
|
|
|
|
// Forward to OpenAI API with server-side key
|
|
const openai = new OpenAI({
|
|
apiKey: process.env.OPENAI_API_KEY,
|
|
});
|
|
|
|
// Forward request to OpenAI Realtime API
|
|
// Implementation depends on the exact endpoint being called
|
|
// This is a simplified example
|
|
|
|
return NextResponse.json({
|
|
message: 'Proxy implementation needed',
|
|
});
|
|
|
|
} catch (error: any) {
|
|
return NextResponse.json(
|
|
{ error: 'Proxy error' },
|
|
{ status: 500 }
|
|
);
|
|
}
|
|
}
|
|
|
|
// ========================================
|
|
// Important Security Notes
|
|
// ========================================
|
|
|
|
/**
|
|
* SECURITY CHECKLIST:
|
|
*
|
|
* 1. ✅ NEVER send OPENAI_API_KEY to browser
|
|
* 2. ✅ Use ephemeral/session tokens with expiration
|
|
* 3. ✅ Implement rate limiting per user/session
|
|
* 4. ✅ Authenticate users before generating tokens
|
|
* 5. ✅ Store session tokens in secure storage (Redis/KV)
|
|
* 6. ✅ Log all voice session creation for monitoring
|
|
* 7. ✅ Set maximum session duration (e.g., 1 hour)
|
|
* 8. ✅ Implement cost controls and usage tracking
|
|
*
|
|
* RECOMMENDED ARCHITECTURE:
|
|
*
|
|
* Browser Client → Next.js Proxy → OpenAI API
|
|
* ↓
|
|
* Session Token (never sees main API key)
|
|
*
|
|
* Alternative: Use OpenAI's official ephemeral key endpoint when available
|
|
*/
|