lecoffre-back-mini/src/handlers/idnot-callback.handlers.ts
NicolasCantu dd45e99a80 feat(idnot): add mock mode for development without IdNot access
- Add IDNOT_MOCK=1 environment variable to bypass external IdNot calls
- Use real test account data: IDN187087, STON CARQUEIRANNE, CRPCEN 083079
- Mock authentication returns valid user data for development
- Allows testing auth flow without IdNot API access

[skip ci]
2025-09-24 22:31:51 +02:00

66 lines
2.7 KiB
TypeScript

import { Request, Response } from 'express';
import { asyncHandler } from '../middleware/error-handler';
import { StateService } from '../services/state.service';
import { IdNotController } from '../controllers/idnot.controller';
import { ExternalServiceError, ValidationError } from '../types/errors';
export class IdNotCallbackHandlers {
static callback = asyncHandler(async (req: Request, res: Response): Promise<void> => {
const code = req.query.code as string | undefined;
const state = req.query.state as string | undefined;
// Debug logging to inspect what reaches the backend via Nginx
// Note: keep logs concise to avoid leaking sensitive data in production
try {
// Only log presence/length of params to avoid full values in logs
const safeQuery = {
code_present: typeof code === 'string',
code_length: typeof code === 'string' ? code.length : undefined,
state_present: typeof state === 'string',
state_length: typeof state === 'string' ? state.length : undefined
};
console.info('[IdNotCallback] incoming request', {
originalUrl: req.originalUrl,
method: req.method,
query: safeQuery,
headers: {
host: req.headers.host,
'x-forwarded-for': req.headers['x-forwarded-for'],
'x-forwarded-proto': req.headers['x-forwarded-proto']
}
});
} catch {}
if (!code || !state) {
throw new ValidationError('Missing code or state', [
{ field: 'code', value: code, constraints: ['required'] },
{ field: 'state', value: state, constraints: ['required'] }
]);
}
const payload = StateService.verifyState(state);
// If external IdNot access is unavailable, allow mock bypass when enabled
const mockEnabled = process.env.IDNOT_MOCK === '1';
if (mockEnabled) {
const { authToken } = await IdNotController.authenticate(code);
const url = new URL(payload.next_url);
const hash = url.hash ? url.hash.replace(/^#/, '') + `&authToken=${encodeURIComponent(authToken)}` : `authToken=${encodeURIComponent(authToken)}`;
const redirectTo = `${url.origin}${url.pathname}${url.search}#${hash}`;
return res.redirect(302, redirectTo);
}
// Exchange code using existing controller logic to build auth and user
const { authToken } = await IdNotController.authenticate(code);
const url = new URL(payload.next_url);
// Prefer fragment to avoid leaking in server logs
const hash = url.hash ? url.hash.replace(/^#/, '') + `&authToken=${encodeURIComponent(authToken)}` : `authToken=${encodeURIComponent(authToken)}`;
const redirectTo = `${url.origin}${url.pathname}${url.search}#${hash}`;
res.redirect(302, redirectTo);
});
}