53 lines
2.0 KiB
TypeScript
53 lines
2.0 KiB
TypeScript
import { Request, Response, NextFunction } from 'express';
|
|
import jwt, { JwtPayload } from 'jsonwebtoken';
|
|
import { jwksClientInstance } from './jwks-client';
|
|
import { extractBearerToken } from './extract-bearer-token';
|
|
import { env } from '../config/env';
|
|
import { ServiceAuthContext } from './auth.types';
|
|
|
|
interface ServiceTokenPayload extends JwtPayload {
|
|
// "azp" (authorized party) e' il client_id del client che ha ottenuto il token
|
|
// via client credentials grant: e' il modo standard per riconoscere quale
|
|
// servizio sta chiamando in una chiamata machine-to-machine.
|
|
azp?: string;
|
|
}
|
|
|
|
// Middleware da usare al posto di (non insieme a) verifyToken sulle route
|
|
// machine-to-machine, ad es. POST /eventi/:id/risorse chiamata da altri backend
|
|
// (scouthub-attivita-be, scouthub-magazzino-be, ...) con le proprie credenziali
|
|
// di servizio. Il client chiamante deve essere elencato in
|
|
// KEYCLOAK_AUTHORIZED_SERVICE_CLIENTS.
|
|
export async function verifyServiceToken(req: Request, res: Response, next: NextFunction): Promise<void> {
|
|
const token = extractBearerToken(req);
|
|
if (!token) {
|
|
res.status(401).json({ message: 'Token mancante' });
|
|
return;
|
|
}
|
|
|
|
try {
|
|
const decoded = jwt.decode(token, { complete: true });
|
|
if (!decoded || !decoded.header.kid) {
|
|
throw new Error("Header del token privo di 'kid'");
|
|
}
|
|
|
|
const signingKey = await jwksClientInstance.getSigningKey(decoded.header.kid);
|
|
const payload = jwt.verify(token, signingKey.getPublicKey(), { algorithms: ['RS256'] });
|
|
|
|
if (typeof payload === 'string') {
|
|
throw new Error('Payload del token non valido');
|
|
}
|
|
|
|
const clientId = (payload as ServiceTokenPayload).azp;
|
|
if (!clientId || !env.keycloak.authorizedServiceClients.includes(clientId)) {
|
|
res.status(403).json({ message: 'Client di servizio non autorizzato' });
|
|
return;
|
|
}
|
|
|
|
const service: ServiceAuthContext = { clientId };
|
|
req.service = service;
|
|
next();
|
|
} catch {
|
|
res.status(401).json({ message: 'Token non valido' });
|
|
}
|
|
}
|