summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPawel Zelawski <pawel.zelawski@outlook.com>2025-04-10 14:39:56 +0200
committerPawel Zelawski <pawel.zelawski@outlook.com>2025-04-10 14:39:56 +0200
commitd56c6a52f1dd1e539acd7a9cd45e2d32a2439ce8 (patch)
tree0aef7f0018e53ca66274d8ba355ed4cd45fbef76
parentd858bd4e0fc7aaba15509a17b35138e843667bc1 (diff)
fix: resolve TypeScript errors in Express route handlers
- Add NextFunction type import from express - Restructure route handlers to use proper Express middleware pattern - Wrap async functions in IIFE for proper error handling - Add proper error handling with next function - Maintain existing functionality while satisfying TypeScript type system This fixes TS2769 errors related to route handler type definitions in /api/digiid/start and /api/digiid/callback endpoints.
-rw-r--r--src/server/main.ts268
1 files changed, 136 insertions, 132 deletions
diff --git a/src/server/main.ts b/src/server/main.ts
index a62e19e..96afb0e 100644
--- a/src/server/main.ts
+++ b/src/server/main.ts
@@ -1,5 +1,5 @@
import dotenv from 'dotenv';
-import express, { Request, Response } from 'express';
+import express, { Request, Response, NextFunction } from 'express';
import { randomBytes } from 'crypto';
import qrcode from 'qrcode';
import { generateDigiIDUri, verifyDigiIDCallback } from 'digiid-ts';
@@ -28,152 +28,156 @@ console.log(`Attempting to listen on port: ${PORT}`);
console.log(`Configured PUBLIC_URL: ${process.env.PUBLIC_URL}`);
// Endpoint to initiate the DigiID authentication flow
-app.get('/api/digiid/start', async (req: Request, res: Response) => {
- try {
- const sessionId = randomBytes(16).toString('hex');
- const nonce = randomBytes(16).toString('hex');
-
- const publicUrl = process.env.PUBLIC_URL;
- if (!publicUrl) {
- console.error('PUBLIC_URL environment variable is not set.');
- return res.status(500).json({ error: 'Server configuration error: PUBLIC_URL is missing.' });
+app.get('/api/digiid/start', (req: Request, res: Response, next: NextFunction) => {
+ (async () => {
+ try {
+ const sessionId = randomBytes(16).toString('hex');
+ const nonce = randomBytes(16).toString('hex');
+
+ const publicUrl = process.env.PUBLIC_URL;
+ if (!publicUrl) {
+ console.error('PUBLIC_URL environment variable is not set.');
+ return res.status(500).json({ error: 'Server configuration error: PUBLIC_URL is missing.' });
+ }
+
+ let callbackUrl: string;
+ try {
+ const baseUrl = new URL(publicUrl);
+ callbackUrl = new URL('/api/digiid/callback', baseUrl).toString();
+ } catch (error) {
+ console.error('Invalid PUBLIC_URL format:', publicUrl, error);
+ return res.status(500).json({ error: 'Server configuration error: Invalid PUBLIC_URL format.' });
+ }
+
+ const unsecure = callbackUrl.startsWith('http://');
+ // Use the actual function from digiid-ts
+ const digiIdUri = generateDigiIDUri({ callbackUrl, unsecure, nonce });
+ console.log(`Generated DigiID URI: ${digiIdUri} for session ${sessionId}`);
+
+ sessionStore.set(sessionId, { nonce, status: 'pending' });
+ nonceToSessionMap.set(nonce, sessionId);
+ console.log(`Stored pending session: ${sessionId}, nonce: ${nonce}`);
+
+ const qrCodeDataUrl = await qrcode.toDataURL(digiIdUri);
+ res.json({ sessionId, qrCodeDataUrl });
+
+ } catch (error) {
+ console.error('Error in /api/digiid/start:', error);
+ // Ensure response is sent even on error
+ if (!res.headersSent) { // Check if response hasn't already been sent
+ if (error instanceof Error) {
+ res.status(400).json({ error: `Failed to generate URI: ${error.message}` });
+ } else {
+ res.status(500).json({ error: 'Internal server error during start' });
+ }
+ }
}
+ })().catch(next);
+});
- let callbackUrl: string;
+// Callback endpoint for the DigiID mobile app
+app.post('/api/digiid/callback', (req: Request, res: Response, next: NextFunction) => {
+ (async () => {
+ const { address, uri, signature } = req.body;
+
+ // Basic validation of received data
+ if (!address || !uri || !signature) {
+ console.warn('Callback missing required fields.', { address, uri, signature });
+ // Wallet doesn't expect a body on failure, just non-200. Status only for logging/debug.
+ return res.status(400).send('Missing required callback parameters.');
+ }
+
+ const callbackData = { address, uri, signature };
+ console.log('Received callback:', callbackData);
+
+ // --- Nonce Extraction and Session Lookup ---
+ let receivedNonce: string | null = null;
try {
- const baseUrl = new URL(publicUrl);
- callbackUrl = new URL('/api/digiid/callback', baseUrl).toString();
+ // DigiID URIs need scheme replaced for standard URL parsing
+ const parsableUri = uri.replace(/^digiid:/, 'http:');
+ const parsedUri = new URL(parsableUri);
+ receivedNonce = parsedUri.searchParams.get('x');
} catch (error) {
- console.error('Invalid PUBLIC_URL format:', publicUrl, error);
- return res.status(500).json({ error: 'Server configuration error: Invalid PUBLIC_URL format.' });
+ console.warn('Error parsing received URI:', uri, error);
+ return res.status(400).send('Invalid URI format.');
}
- const unsecure = callbackUrl.startsWith('http://');
- // Use the actual function from digiid-ts
- const digiIdUri = generateDigiIDUri({ callbackUrl, unsecure, nonce });
- console.log(`Generated DigiID URI: ${digiIdUri} for session ${sessionId}`);
-
- sessionStore.set(sessionId, { nonce, status: 'pending' });
- nonceToSessionMap.set(nonce, sessionId);
- console.log(`Stored pending session: ${sessionId}, nonce: ${nonce}`);
-
- const qrCodeDataUrl = await qrcode.toDataURL(digiIdUri);
- res.json({ sessionId, qrCodeDataUrl });
-
- } catch (error) {
- console.error('Error in /api/digiid/start:', error);
- // Ensure response is sent even on error
- if (!res.headersSent) { // Check if response hasn't already been sent
- if (error instanceof Error) {
- res.status(400).json({ error: `Failed to generate URI: ${error.message}` });
- } else {
- res.status(500).json({ error: 'Internal server error during start' });
- }
+ if (!receivedNonce) {
+ console.warn('Nonce (x parameter) not found in received URI:', uri);
+ return res.status(400).send('Nonce not found in URI.');
}
- }
-});
-// Callback endpoint for the DigiID mobile app
-app.post('/api/digiid/callback', async (req: Request, res: Response) => {
- const { address, uri, signature } = req.body;
-
- // Basic validation of received data
- if (!address || !uri || !signature) {
- console.warn('Callback missing required fields.', { address, uri, signature });
- // Wallet doesn't expect a body on failure, just non-200. Status only for logging/debug.
- return res.status(400).send('Missing required callback parameters.');
- }
+ const sessionId = nonceToSessionMap.get(receivedNonce);
+ if (!sessionId) {
+ console.warn('Session not found for received nonce:', receivedNonce);
+ // Nonce might be expired or invalid
+ return res.status(404).send('Session not found or expired for this nonce.');
+ }
- const callbackData = { address, uri, signature };
- console.log('Received callback:', callbackData);
-
- // --- Nonce Extraction and Session Lookup ---
- let receivedNonce: string | null = null;
- try {
- // DigiID URIs need scheme replaced for standard URL parsing
- const parsableUri = uri.replace(/^digiid:/, 'http:');
- const parsedUri = new URL(parsableUri);
- receivedNonce = parsedUri.searchParams.get('x');
- } catch (error) {
- console.warn('Error parsing received URI:', uri, error);
- return res.status(400).send('Invalid URI format.');
- }
+ // Retrieve the session *before* the try/finally block for verification
+ const session = sessionStore.get(sessionId);
+ if (!session) {
+ console.error(`Critical: Session data missing for ${sessionId} despite nonce match.`);
+ if (!res.headersSent) res.status(500).send('Internal server error: Session data missing.');
+ return; // Explicitly return void
+ }
+ if (session.status !== 'pending') {
+ console.warn('Session already processed:', sessionId, session.status);
+ if (!res.headersSent) res.status(200).send('Session already processed.');
+ return; // Explicitly return void
+ }
- if (!receivedNonce) {
- console.warn('Nonce (x parameter) not found in received URI:', uri);
- return res.status(400).send('Nonce not found in URI.');
- }
+ // --- Verification ---
+ let expectedCallbackUrl: string;
+ try {
+ const publicUrl = process.env.PUBLIC_URL;
+ if (!publicUrl) throw new Error('PUBLIC_URL environment variable is not configured on the server.');
+ expectedCallbackUrl = new URL('/api/digiid/callback', publicUrl).toString();
+ } catch (error) {
+ console.error('Server configuration error constructing expected callback URL:', error);
+ session.status = 'failed';
+ session.error = 'Server configuration error preventing verification.';
+ sessionStore.set(sessionId, session);
+ // Respond 200 OK as per protocol
+ if (!res.headersSent) res.status(200).send();
+ return; // Explicitly return void
+ }
- const sessionId = nonceToSessionMap.get(receivedNonce);
- if (!sessionId) {
- console.warn('Session not found for received nonce:', receivedNonce);
- // Nonce might be expired or invalid
- return res.status(404).send('Session not found or expired for this nonce.');
- }
+ const verifyOptions = { expectedCallbackUrl, expectedNonce: session.nonce };
- // Retrieve the session *before* the try/finally block for verification
- const session = sessionStore.get(sessionId);
- if (!session) {
- console.error(`Critical: Session data missing for ${sessionId} despite nonce match.`);
- if (!res.headersSent) res.status(500).send('Internal server error: Session data missing.');
- return; // Explicitly return void
- }
- if (session.status !== 'pending') {
- console.warn('Session already processed:', sessionId, session.status);
- if (!res.headersSent) res.status(200).send('Session already processed.');
- return; // Explicitly return void
- }
+ try {
+ console.log('Attempting to verify callback with:', { callbackData, verifyOptions });
+ await verifyDigiIDCallback(callbackData, verifyOptions);
- // --- Verification ---
- let expectedCallbackUrl: string;
- try {
- const publicUrl = process.env.PUBLIC_URL;
- if (!publicUrl) throw new Error('PUBLIC_URL environment variable is not configured on the server.');
- expectedCallbackUrl = new URL('/api/digiid/callback', publicUrl).toString();
- } catch (error) {
- console.error('Server configuration error constructing expected callback URL:', error);
- session.status = 'failed';
- session.error = 'Server configuration error preventing verification.';
- sessionStore.set(sessionId, session);
- // Respond 200 OK as per protocol
- if (!res.headersSent) res.status(200).send();
- return; // Explicitly return void
- }
+ // Success case (no throw from verifyDigiIDCallback)
+ console.log(`Verification successful for session ${sessionId}, address: ${address}`);
+ session.status = 'success';
+ session.address = address; // Store the verified address
+ session.error = undefined; // Clear any previous error
+ nonceToSessionMap.delete(session.nonce); // Clean up nonce map only on success
- const verifyOptions = { expectedCallbackUrl, expectedNonce: session.nonce };
-
- try {
- console.log('Attempting to verify callback with:', { callbackData, verifyOptions });
- await verifyDigiIDCallback(callbackData, verifyOptions);
-
- // Success case (no throw from verifyDigiIDCallback)
- console.log(`Verification successful for session ${sessionId}, address: ${address}`);
- session.status = 'success';
- session.address = address; // Store the verified address
- session.error = undefined; // Clear any previous error
- nonceToSessionMap.delete(session.nonce); // Clean up nonce map only on success
-
- } catch (error) {
- // Failure case (verifyDigiIDCallback threw an error)
- console.warn(`Verification failed for session ${sessionId}:`, error);
- session.status = 'failed';
- if (error instanceof Error) {
- session.error = error.message;
- } else {
- session.error = 'An unknown verification error occurred.';
- }
- // Optionally cleanup nonce map on failure too, depending on policy
- // nonceToSessionMap.delete(session.nonce);
- } finally {
- // Update store and respond 200 OK in all cases (after try/catch)
- sessionStore.set(sessionId, session);
- console.log(`Final session state for ${sessionId}:`, session);
- // Ensure response is sent if not already done (e.g. in case of unexpected error before finally)
- if (!res.headersSent) {
- res.status(200).send();
+ } catch (error) {
+ // Failure case (verifyDigiIDCallback threw an error)
+ console.warn(`Verification failed for session ${sessionId}:`, error);
+ session.status = 'failed';
+ if (error instanceof Error) {
+ session.error = error.message;
+ } else {
+ session.error = 'An unknown verification error occurred.';
+ }
+ // Optionally cleanup nonce map on failure too, depending on policy
+ // nonceToSessionMap.delete(session.nonce);
+ } finally {
+ // Update store and respond 200 OK in all cases (after try/catch)
+ sessionStore.set(sessionId, session);
+ console.log(`Final session state for ${sessionId}:`, session);
+ // Ensure response is sent if not already done (e.g. in case of unexpected error before finally)
+ if (!res.headersSent) {
+ res.status(200).send();
+ }
+ // No explicit return needed here as it's the end of the function
}
- // No explicit return needed here as it's the end of the function
- }
+ })().catch(next);
});
// Endpoint to check the status of an authentication session