fix: Use Deepgram grantToken() for secure temporary voice tokens
Updated the voice-token API route to use deepgram.auth.grantToken() consistently in all environments. This generates secure 30-second temporary tokens instead of exposing the main API key. - Removed development/production split for consistency - Now uses grantToken() method in all environments - Requires API key with "Member" or higher permissions - Improved security by using short-lived tokens - Updated documentation to reflect new requirements 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -2,10 +2,14 @@ import { NextRequest, NextResponse } from 'next/server';
|
|||||||
import { createClient } from '@deepgram/sdk';
|
import { createClient } from '@deepgram/sdk';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This API route generates a short-lived, temporary API key
|
* This API route generates a short-lived, temporary access token
|
||||||
* for a client to connect directly to Deepgram's WebSocket.
|
* for a client to connect directly to Deepgram's WebSocket.
|
||||||
* This avoids exposing our main API key and bypasses
|
*
|
||||||
* serverless WebSocket limitations.
|
* The temporary token has a 30-second TTL and provides better security
|
||||||
|
* than exposing the main API key. This approach also bypasses
|
||||||
|
* serverless WebSocket limitations by allowing direct client connections.
|
||||||
|
*
|
||||||
|
* Requires: API key with "Member" or higher permissions
|
||||||
*/
|
*/
|
||||||
export async function POST(request: NextRequest) {
|
export async function POST(request: NextRequest) {
|
||||||
const deepgramApiKey = process.env.DEEPGRAM_API_KEY;
|
const deepgramApiKey = process.env.DEEPGRAM_API_KEY;
|
||||||
@@ -20,25 +24,24 @@ export async function POST(request: NextRequest) {
|
|||||||
const deepgram = createClient(deepgramApiKey);
|
const deepgram = createClient(deepgramApiKey);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Create a new, temporary key with 'member' permissions
|
console.log('[Voice Token] Generating temporary token...');
|
||||||
// that expires in 1 minute (60 seconds).
|
const { result, error } = await deepgram.auth.grantToken();
|
||||||
const response = await deepgram.manage.createProjectKey(
|
|
||||||
process.env.DEEPGRAM_PROJECT_ID || '',
|
|
||||||
{
|
|
||||||
comment: 'Temporary key for Ponderants user',
|
|
||||||
scopes: ['member'],
|
|
||||||
time_to_live_in_seconds: 60,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response || !response.key) {
|
if (error) {
|
||||||
throw new Error('Failed to create temporary key');
|
console.error('[Voice Token] Deepgram error:', error);
|
||||||
|
throw new Error(`Deepgram error: ${error.message}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Send the temporary key back to the client
|
if (!result || !result.access_token) {
|
||||||
return NextResponse.json({ key: response.key });
|
console.error('[Voice Token] No token in response:', result);
|
||||||
|
throw new Error('No token in response');
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('[Voice Token] ✓ Token generated successfully');
|
||||||
|
console.log(`[Voice Token] Token expires in ${result.expires_in} seconds`);
|
||||||
|
return NextResponse.json({ key: result.access_token });
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Error creating Deepgram key:', error);
|
console.error('[Voice Token] Error creating Deepgram token:', error);
|
||||||
return NextResponse.json(
|
return NextResponse.json(
|
||||||
{ error: 'Failed to generate voice token' },
|
{ error: 'Failed to generate voice token' },
|
||||||
{ status: 500 }
|
{ status: 500 }
|
||||||
|
|||||||
Reference in New Issue
Block a user