diff --git a/packages/backend-for-frontend/src/services/authorizationService.ts b/packages/backend-for-frontend/src/services/authorizationService.ts index 7edca0cd2c..ee808d642a 100644 --- a/packages/backend-for-frontend/src/services/authorizationService.ts +++ b/packages/backend-for-frontend/src/services/authorizationService.ts @@ -18,6 +18,7 @@ import { decodeJwtToken, userRoles, verifyJwtToken, + getJwksClient, } from "pagopa-interop-commons"; import { TenantId, genericError, unsafeBrandId } from "pagopa-interop-models"; import { config } from "../config/config.js"; @@ -53,6 +54,8 @@ export function authorizationServiceBuilder( allowList: string[], rateLimiter: RateLimiter ) { + const jwksClients = getJwksClient(); + const readJwt = async ( identityToken: string, logger: Logger @@ -61,7 +64,7 @@ export function authorizationServiceBuilder( sessionClaims: SessionClaims; selfcareId: string; }> => { - const verified = await verifyJwtToken(identityToken, logger); + const verified = await verifyJwtToken(identityToken, jwksClients, logger); if (!verified) { throw tokenVerificationFailed(); } diff --git a/packages/commons/src/auth/authenticationMiddleware.ts b/packages/commons/src/auth/authenticationMiddleware.ts index 14d222ec20..e1dd31552f 100644 --- a/packages/commons/src/auth/authenticationMiddleware.ts +++ b/packages/commons/src/auth/authenticationMiddleware.ts @@ -7,7 +7,7 @@ import { unauthorizedError, } from "pagopa-interop-models"; import { P, match } from "ts-pattern"; -import { ExpressContext } from "../index.js"; +import { ExpressContext, getJwksClient } from "../index.js"; import { Logger, logger } from "../logging/index.js"; import { AuthData } from "./authData.js"; import { Headers } from "./headers.js"; @@ -18,6 +18,8 @@ const makeApiProblem = makeApiProblemBuilder({}); export const authenticationMiddleware: ZodiosRouterContextRequestHandler< ExpressContext > = async (req, res, next): Promise => { + const jwksClients = getJwksClient(); + const addCtxAuthData = async ( authHeader: string, logger: Logger @@ -34,7 +36,7 @@ export const authenticationMiddleware: ZodiosRouterContextRequestHandler< } const jwtToken = authorizationHeader[1]; - const valid = await verifyJwtToken(jwtToken, logger); + const valid = await verifyJwtToken(jwtToken, jwksClients, logger); if (!valid) { throw unauthorizedError("Invalid token"); } diff --git a/packages/commons/src/auth/jwk.ts b/packages/commons/src/auth/jwk.ts index a59bdb12b9..22890675e4 100644 --- a/packages/commons/src/auth/jwk.ts +++ b/packages/commons/src/auth/jwk.ts @@ -5,6 +5,8 @@ import { notAllowedCertificateException, notAllowedPrivateKeyException, } from "pagopa-interop-models"; +import jwksClient, { JwksClient } from "jwks-rsa"; +import { JWTConfig } from "../config/index.js"; export const decodeBase64ToPem = (base64String: string): string => { try { @@ -64,3 +66,17 @@ export function sortJWK(jwk: JsonWebKey): JsonWebKey { {} ); } + +export function getJwksClient(): JwksClient[] { + const config = JWTConfig.parse(process.env); + return config.wellKnownUrls.map((url) => + jwksClient({ + cache: true, + cacheMaxEntries: 5, + timeout: 30000, + cacheMaxAge: 600000, // 10 minutes + jwksRequestsPerMinute: 10, + jwksUri: url, + }) + ); +} diff --git a/packages/commons/src/auth/jwt.ts b/packages/commons/src/auth/jwt.ts index 6d247dd85d..a188254298 100644 --- a/packages/commons/src/auth/jwt.ts +++ b/packages/commons/src/auth/jwt.ts @@ -61,30 +61,26 @@ const getKey = async ( export const verifyJwtToken = async ( jwtToken: string, + jwksClients: jwksClient.JwksClient[], logger: Logger ): Promise => { - const config = JWTConfig.parse(process.env); - const clients = config.wellKnownUrls.map((url) => - jwksClient({ - jwksUri: url, - }) - ); - try { + const { acceptedAudiences } = JWTConfig.parse(process.env); + const jwtHeader = decodeJwtTokenHeaders(jwtToken, logger); if (!jwtHeader?.kid) { logger.warn("Token verification failed: missing kid"); return Promise.reject(false); } - const secret: Secret = await getKey(clients, jwtHeader.kid, logger); + const secret: Secret = await getKey(jwksClients, jwtHeader.kid, logger); return new Promise((resolve, _reject) => { jwt.verify( jwtToken, secret, { - audience: config.acceptedAudiences, + audience: acceptedAudiences, }, function (err, _decoded) { if (err) {