@@ -37,47 +37,14 @@ const CONFIG = {
3737} ;
3838
3939// ============================================================================
40- // SESSION AUTH - JWT tokens with page nonce for production security
40+ // SESSION AUTH - JWT tokens for production security
4141// ============================================================================
4242
4343const SESSION_SECRET =
4444 process . env . SESSION_SECRET || crypto . randomBytes ( 32 ) . toString ( 'hex' ) ;
45- const REQUIRE_NONCE = ! ! process . env . SESSION_SECRET ;
4645
47- const sessionNonces = new Map ( ) ;
48- const NONCE_TTL_MS = 5 * 60 * 1000 ;
4946const JWT_EXPIRY = '1h' ;
5047
51- function generateNonce ( ) {
52- const nonce = crypto . randomBytes ( 16 ) . toString ( 'hex' ) ;
53- sessionNonces . set ( nonce , Date . now ( ) + NONCE_TTL_MS ) ;
54- return nonce ;
55- }
56-
57- function consumeNonce ( nonce ) {
58- const expiry = sessionNonces . get ( nonce ) ;
59- if ( ! expiry ) return false ;
60- sessionNonces . delete ( nonce ) ;
61- return Date . now ( ) < expiry ;
62- }
63-
64- setInterval ( ( ) => {
65- const now = Date . now ( ) ;
66- for ( const [ nonce , expiry ] of sessionNonces ) {
67- if ( now >= expiry ) sessionNonces . delete ( nonce ) ;
68- }
69- } , 60_000 ) ;
70-
71- let indexHtmlTemplate = null ;
72- try {
73- indexHtmlTemplate = fs . readFileSync (
74- path . join ( __dirname , 'frontend' , 'dist' , 'index.html' ) ,
75- 'utf-8'
76- ) ;
77- } catch {
78- // No built frontend (dev mode)
79- }
80-
8148/**
8249 * Validates JWT from WebSocket subprotocol: access_token.<jwt>
8350 * Returns the token string if valid, null if invalid.
@@ -120,40 +87,14 @@ app.use(cors());
12087// ============================================================================
12188
12289/**
123- * GET / — Serve index.html with injected session nonce (production only)
124- */
125- app . get ( '/' , ( req , res ) => {
126- if ( ! indexHtmlTemplate ) {
127- return res . status ( 404 ) . send ( 'Frontend not built. Run make build first.' ) ;
128- }
129- const nonce = generateNonce ( ) ;
130- const html = indexHtmlTemplate . replace (
131- '</head>' ,
132- `<meta name="session-nonce" content="${ nonce } ">\n</head>`
133- ) ;
134- res . type ( 'html' ) . send ( html ) ;
135- } ) ;
136-
137- /**
138- * GET /api/session — Issues a JWT. In production, requires valid nonce.
90+ * GET /api/session — Issues a signed JWT for session authentication.
13991 */
14092app . get ( '/api/session' , ( req , res ) => {
141- if ( REQUIRE_NONCE ) {
142- const nonce = req . headers [ 'x-session-nonce' ] ;
143- if ( ! nonce || ! consumeNonce ( nonce ) ) {
144- return res . status ( 403 ) . json ( {
145- error : {
146- type : 'AuthenticationError' ,
147- code : 'INVALID_NONCE' ,
148- message : 'Valid session nonce required. Please refresh the page.' ,
149- } ,
150- } ) ;
151- }
152- }
153-
154- const token = jwt . sign ( { iat : Math . floor ( Date . now ( ) / 1000 ) } , SESSION_SECRET , {
155- expiresIn : JWT_EXPIRY ,
156- } ) ;
93+ const token = jwt . sign (
94+ { iat : Math . floor ( Date . now ( ) / 1000 ) } ,
95+ SESSION_SECRET ,
96+ { expiresIn : JWT_EXPIRY }
97+ ) ;
15798 res . json ( { token } ) ;
15899} ) ;
159100
@@ -358,7 +299,7 @@ server.listen(CONFIG.port, CONFIG.host, () => {
358299 console . log ( "\n" + "=" . repeat ( 70 ) ) ;
359300 console . log ( `🚀 Backend API Server running at http://localhost:${ CONFIG . port } ` ) ;
360301 console . log ( "" ) ;
361- console . log ( `📡 GET /api/session${ REQUIRE_NONCE ? ' (nonce required)' : '' } ` ) ;
302+ console . log ( `📡 GET /api/session` ) ;
362303 console . log ( `📡 WS /api/voice-agent (auth required)` ) ;
363304 console . log ( `📡 GET /api/metadata` ) ;
364305 console . log ( "=" . repeat ( 70 ) + "\n" ) ;
0 commit comments