diff --git a/.changeset/unlucky-wolves-accept.md b/.changeset/unlucky-wolves-accept.md new file mode 100644 index 00000000..d86c5963 --- /dev/null +++ b/.changeset/unlucky-wolves-accept.md @@ -0,0 +1,5 @@ +--- +"io-wallet-user-func": patch +--- + +Add io-web magic link jwt validation diff --git a/apps/io-wallet-user-func/local.settings.json.example b/apps/io-wallet-user-func/local.settings.json.example index b3b3e322..8bcf9f2c 100644 --- a/apps/io-wallet-user-func/local.settings.json.example +++ b/apps/io-wallet-user-func/local.settings.json.example @@ -33,6 +33,8 @@ "HubSpidLoginJwtPubKey": "HUB_SPID_LOGIN_JWT_PUB_KEY", "HubSpidLoginJwtIssuer": "HUB_SPID_LOGIN_JWT_ISSUER", "HubSpidLoginClientBaseUrl": "HUB_SPID_LOGIN_CLIENT_BASE_URL", + "ExchangeJwtIssuer": "EXCHANGE_JWT_ISSUER", + "ExchangeJwtPubKey": "EXCHANGE_JWT_PUB_KEY", "TrialSystemApiBaseURL": "TRIAL_SYSTEM_API_BASE_URL", "TrialSystemApiKey": "TRIAL_SYSTEM_API_KEY", "TrialSystemTrialId": "TRIAL_SYSTEM_TRIAL_ID", diff --git a/apps/io-wallet-user-func/src/app/config.ts b/apps/io-wallet-user-func/src/app/config.ts index 3305e319..41fe3924 100644 --- a/apps/io-wallet-user-func/src/app/config.ts +++ b/apps/io-wallet-user-func/src/app/config.ts @@ -80,13 +80,19 @@ export type PdvTokenizerApiClientConfig = t.TypeOf< typeof PdvTokenizerApiClientConfig >; -const HubSpidLoginConfig = t.type({ - clientBaseUrl: NonEmptyString, - jwtIssuer: NonEmptyString, - jwtPubKey: NonEmptyString, +const JwtValidatorConfig = t.type({ + exchange: t.type({ + jwtIssuer: NonEmptyString, + jwtPubKey: NonEmptyString, + }), + hubSpidLogin: t.type({ + clientBaseUrl: NonEmptyString, + jwtIssuer: NonEmptyString, + jwtPubKey: NonEmptyString, + }), }); -export type HubSpidLoginConfig = t.TypeOf; +export type JwtValidatorConfig = t.TypeOf; const TrialSystemApiClientConfig = t.type({ apiKey: t.string, @@ -116,7 +122,7 @@ export const Config = t.type({ azure: AzureConfiguration, crypto: CryptoConfiguration, federationEntity: FederationEntityMetadata, - hubSpidLogin: HubSpidLoginConfig, + jwtValidator: JwtValidatorConfig, pdvTokenizer: PdvTokenizerApiClientConfig, pidIssuer: PidIssuerApiClientConfig, trialSystem: TrialSystemApiClientConfig, @@ -124,46 +130,6 @@ export const Config = t.type({ export type Config = t.TypeOf; -export const getConfigFromEnvironment: RE.ReaderEither< - NodeJS.ProcessEnv, - Error, - Config -> = pipe( - RE.Do, - RE.bind("federationEntity", () => getFederationEntityConfigFromEnvironment), - RE.bind("crypto", () => getCryptoConfigFromEnvironment), - RE.bind( - "attestationService", - () => getAttestationServiceConfigFromEnvironment, - ), - RE.bind("azure", () => getAzureConfigFromEnvironment), - RE.bind("pdvTokenizer", () => getPdvTokenizerConfigFromEnvironment), - RE.bind("hubSpidLogin", () => getHubSpidLoginConfigFromEnvironment), - RE.bind("trialSystem", () => getTrialSystemConfigFromEnvironment), - RE.bind("pidIssuer", () => getPidIssuerConfigFromEnvironment), - RE.map( - ({ - attestationService, - azure, - crypto, - federationEntity, - hubSpidLogin, - pdvTokenizer, - pidIssuer, - trialSystem, - }) => ({ - attestationService, - azure, - crypto, - federationEntity, - hubSpidLogin, - pdvTokenizer, - pidIssuer, - trialSystem, - }), - ), -); - const getFederationEntityConfigFromEnvironment: RE.ReaderEither< NodeJS.ProcessEnv, Error, @@ -313,12 +279,17 @@ const getPdvTokenizerConfigFromEnvironment: RE.ReaderEither< ), ); -const getHubSpidLoginConfigFromEnvironment: RE.ReaderEither< +const getJwtValidatorConfigFromEnvironment: RE.ReaderEither< NodeJS.ProcessEnv, Error, - HubSpidLoginConfig + JwtValidatorConfig > = pipe( sequenceS(RE.Apply)({ + exchangeJwtIssuer: readFromEnvironment("ExchangeJwtIssuer"), + exchangeJwtPubKey: pipe( + readFromEnvironment("ExchangeJwtPubKey"), + RE.map(decodeBase64String), + ), hubSpidLoginClientBaseUrl: readFromEnvironment("HubSpidLoginClientBaseUrl"), hubSpidLoginJwtIssuer: readFromEnvironment("HubSpidLoginJwtIssuer"), hubSpidLoginJwtPubKey: pipe( @@ -328,18 +299,26 @@ const getHubSpidLoginConfigFromEnvironment: RE.ReaderEither< }), RE.map( ({ + exchangeJwtIssuer, + exchangeJwtPubKey, hubSpidLoginClientBaseUrl, hubSpidLoginJwtIssuer, hubSpidLoginJwtPubKey, }) => ({ - clientBaseUrl: hubSpidLoginClientBaseUrl, - jwtIssuer: hubSpidLoginJwtIssuer, - jwtPubKey: hubSpidLoginJwtPubKey, + exchange: { + jwtIssuer: exchangeJwtIssuer, + jwtPubKey: exchangeJwtPubKey, + }, + hubSpidLogin: { + clientBaseUrl: hubSpidLoginClientBaseUrl, + jwtIssuer: hubSpidLoginJwtIssuer, + jwtPubKey: hubSpidLoginJwtPubKey, + }, }), ), RE.chainW((result) => pipe( - HubSpidLoginConfig.decode(result), + JwtValidatorConfig.decode(result), RE.fromEither, RE.mapLeft((errs) => Error(readableReportSimplified(errs))), ), @@ -413,3 +392,20 @@ const getPidIssuerConfigFromEnvironment: RE.ReaderEither< }), ), ); + +export const getConfigFromEnvironment: RE.ReaderEither< + NodeJS.ProcessEnv, + Error, + Config +> = pipe( + sequenceS(RE.Apply)({ + attestationService: getAttestationServiceConfigFromEnvironment, + azure: getAzureConfigFromEnvironment, + crypto: getCryptoConfigFromEnvironment, + federationEntity: getFederationEntityConfigFromEnvironment, + jwtValidator: getJwtValidatorConfigFromEnvironment, + pdvTokenizer: getPdvTokenizerConfigFromEnvironment, + pidIssuer: getPidIssuerConfigFromEnvironment, + trialSystem: getTrialSystemConfigFromEnvironment, + }), +); diff --git a/apps/io-wallet-user-func/src/app/main.ts b/apps/io-wallet-user-func/src/app/main.ts index d109c8de..595d58ca 100644 --- a/apps/io-wallet-user-func/src/app/main.ts +++ b/apps/io-wallet-user-func/src/app/main.ts @@ -9,7 +9,7 @@ import { GetUserByFiscalCodeFunction } from "@/infra/azure/functions/get-user-by import { HealthFunction } from "@/infra/azure/functions/health"; import { SetWalletInstanceStatusFunction } from "@/infra/azure/functions/set-wallet-instance-status"; import { CryptoSigner } from "@/infra/crypto/signer"; -import { hslValidate } from "@/infra/jwt-validator"; +import { jwtValidate } from "@/infra/jwt-validator"; import { PdvTokenizerClient } from "@/infra/pdv-tokenizer/client"; import { PidIssuerClient } from "@/infra/pid-issuer/client"; import { TrialSystemClient } from "@/infra/trial-system/client"; @@ -53,7 +53,7 @@ const pdvTokenizerClient = new PdvTokenizerClient(config.pdvTokenizer); const walletInstanceRepository = new CosmosDbWalletInstanceRepository(database); -const hslJwtValidate = hslValidate(config.hubSpidLogin); +const tokenValidate = jwtValidate(config.jwtValidator); const trialSystemClient = new TrialSystemClient(config.trialSystem); @@ -130,7 +130,7 @@ app.timer("generateEntityConfiguration", { app.http("getCurrentWalletInstanceStatus", { authLevel: "function", handler: GetCurrentWalletInstanceStatusFunction({ - hslJwtValidate, + jwtValidate: tokenValidate, userRepository: pdvTokenizerClient, userTrialSubscriptionRepository: trialSystemClient, walletInstanceRepository, @@ -143,7 +143,7 @@ app.http("setWalletInstanceStatus", { authLevel: "function", handler: SetWalletInstanceStatusFunction({ credentialRepository: pidIssuerClient, - hslJwtValidate, + jwtValidate: tokenValidate, userRepository: pdvTokenizerClient, userTrialSubscriptionRepository: trialSystemClient, walletInstanceRepository, diff --git a/apps/io-wallet-user-func/src/infra/http/handlers/__test__/get-current-wallet-instance.status.spec.ts b/apps/io-wallet-user-func/src/infra/http/handlers/__test__/get-current-wallet-instance.status.spec.ts index 8b8f282b..553ef14f 100644 --- a/apps/io-wallet-user-func/src/infra/http/handlers/__test__/get-current-wallet-instance.status.spec.ts +++ b/apps/io-wallet-user-func/src/infra/http/handlers/__test__/get-current-wallet-instance.status.spec.ts @@ -1,6 +1,6 @@ /* eslint-disable max-lines-per-function */ import { UnauthorizedError } from "@/error"; -import { HslJwtValidate } from "@/jwt-validator"; +import { JwtValidate } from "@/jwt-validator"; import { SubscriptionStateEnum, UserRepository, @@ -51,7 +51,7 @@ describe("GetCurrentWalletInstanceStatusHandler", () => { TE.right({ id: "pdv_id" as NonEmptyString }), }; - const hslJwtValidate: HslJwtValidate = () => + const jwtValidate: JwtValidate = () => TE.right({ fiscal_number: "AAACCC94D55H501P", }); @@ -72,9 +72,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => { }, }; const handler = GetCurrentWalletInstanceStatusHandler({ - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -101,9 +101,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => { ...H.request("https://wallet-provider.example.org"), }; const handler = GetCurrentWalletInstanceStatusHandler({ - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -134,9 +134,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => { }, }; const handler = GetCurrentWalletInstanceStatusHandler({ - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -167,9 +167,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => { }, }; const handler = GetCurrentWalletInstanceStatusHandler({ - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -188,7 +188,7 @@ describe("GetCurrentWalletInstanceStatusHandler", () => { }); it("should return a 422 HTTP response when token does not contain `fiscal_number`", async () => { - const hslJwtValidate: HslJwtValidate = () => + const jwtValidate: JwtValidate = () => TE.right({ foo: "AAACCC94D55H501P", }); @@ -205,9 +205,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => { }, }; const handler = GetCurrentWalletInstanceStatusHandler({ - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -225,8 +225,8 @@ describe("GetCurrentWalletInstanceStatusHandler", () => { }); }); - it("should return a 401 HTTP response on hsl jwt forbidden error", async () => { - const hslJwtValidateThatFails: HslJwtValidate = () => + it("should return a 401 HTTP response on jwt forbidden error", async () => { + const jwtValidateThatFails: JwtValidate = () => TE.left(new UnauthorizedError()); const req = { ...H.request("https://wallet-provider.example.org"), @@ -240,9 +240,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => { }, }; const handler = GetCurrentWalletInstanceStatusHandler({ - hslJwtValidate: hslJwtValidateThatFails, input: req, inputDecoder: H.HttpRequest, + jwtValidate: jwtValidateThatFails, logger, userRepository, userTrialSubscriptionRepository, @@ -281,9 +281,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => { }, }; const handler = GetCurrentWalletInstanceStatusHandler({ - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository: @@ -317,9 +317,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => { }, }; const handler = GetCurrentWalletInstanceStatusHandler({ - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -353,9 +353,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => { }, }; const handler = GetCurrentWalletInstanceStatusHandler({ - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -374,8 +374,8 @@ describe("GetCurrentWalletInstanceStatusHandler", () => { }); }); - it("should return a 500 HTTP response on hslJwtValidate error", async () => { - const hslValidateThatFails: HslJwtValidate = () => + it("should return a 500 HTTP response on jwtValidate error", async () => { + const jwtValidateThatFails: JwtValidate = () => TE.left(new Error("failed on jwtValidationAndDecode!")); const req = { @@ -385,9 +385,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => { }, }; const handler = GetCurrentWalletInstanceStatusHandler({ - hslJwtValidate: hslValidateThatFails, input: req, inputDecoder: H.HttpRequest, + jwtValidate: jwtValidateThatFails, logger, userRepository, userTrialSubscriptionRepository, diff --git a/apps/io-wallet-user-func/src/infra/http/handlers/__test__/set-wallet-instance-status.spec.ts b/apps/io-wallet-user-func/src/infra/http/handlers/__test__/set-wallet-instance-status.spec.ts index 84ff8c85..397fe5e4 100644 --- a/apps/io-wallet-user-func/src/infra/http/handlers/__test__/set-wallet-instance-status.spec.ts +++ b/apps/io-wallet-user-func/src/infra/http/handlers/__test__/set-wallet-instance-status.spec.ts @@ -1,7 +1,7 @@ /* eslint-disable max-lines-per-function */ import { CredentialRepository } from "@/credential"; import { UnauthorizedError } from "@/error"; -import { HslJwtValidate } from "@/jwt-validator"; +import { JwtValidate } from "@/jwt-validator"; import { SubscriptionStateEnum, UserRepository, @@ -34,7 +34,7 @@ describe("SetWalletInstanceStatusHandler", () => { TE.right({ id: "pdv_id" as NonEmptyString }), }; - const hslJwtValidate: HslJwtValidate = () => + const jwtValidate: JwtValidate = () => TE.right({ fiscal_number: "AAACCC94D55H501P", }); @@ -70,9 +70,9 @@ describe("SetWalletInstanceStatusHandler", () => { }; const handler = SetWalletInstanceStatusHandler({ credentialRepository: pidIssuerClient, - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -98,9 +98,9 @@ describe("SetWalletInstanceStatusHandler", () => { }; const handler = SetWalletInstanceStatusHandler({ credentialRepository: pidIssuerClient, - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -132,9 +132,9 @@ describe("SetWalletInstanceStatusHandler", () => { }; const handler = SetWalletInstanceStatusHandler({ credentialRepository: pidIssuerClient, - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -166,9 +166,9 @@ describe("SetWalletInstanceStatusHandler", () => { }; const handler = SetWalletInstanceStatusHandler({ credentialRepository: pidIssuerClient, - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -187,7 +187,7 @@ describe("SetWalletInstanceStatusHandler", () => { }); it("should return a 422 HTTP response when token does not contain `fiscal_number`", async () => { - const hslJwtValidate: HslJwtValidate = () => + const jwtValidate: JwtValidate = () => TE.right({ foo: "AAACCC94D55H501P", }); @@ -205,9 +205,9 @@ describe("SetWalletInstanceStatusHandler", () => { }; const handler = SetWalletInstanceStatusHandler({ credentialRepository: pidIssuerClient, - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -239,9 +239,9 @@ describe("SetWalletInstanceStatusHandler", () => { }; const handler = SetWalletInstanceStatusHandler({ credentialRepository: pidIssuerClient, - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -259,8 +259,8 @@ describe("SetWalletInstanceStatusHandler", () => { }); }); - it("should return a 401 HTTP response on hsl jwt forbidden error", async () => { - const hslJwtValidateThatFails: HslJwtValidate = () => + it("should return a 401 HTTP response on jwt forbidden error", async () => { + const jwtValidateThatFails: JwtValidate = () => TE.left(new UnauthorizedError()); const req = { ...H.request("https://wallet-provider.example.org"), @@ -275,9 +275,9 @@ describe("SetWalletInstanceStatusHandler", () => { }; const handler = SetWalletInstanceStatusHandler({ credentialRepository: pidIssuerClient, - hslJwtValidate: hslJwtValidateThatFails, input: req, inputDecoder: H.HttpRequest, + jwtValidate: jwtValidateThatFails, logger, userRepository, userTrialSubscriptionRepository, @@ -317,9 +317,9 @@ describe("SetWalletInstanceStatusHandler", () => { }; const handler = SetWalletInstanceStatusHandler({ credentialRepository: pidIssuerClient, - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository: @@ -358,9 +358,9 @@ describe("SetWalletInstanceStatusHandler", () => { }; const handler = SetWalletInstanceStatusHandler({ credentialRepository: pidIssuerClient, - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository: userTrialSubscriptionRepositoryThatFails, @@ -397,9 +397,9 @@ describe("SetWalletInstanceStatusHandler", () => { }; const handler = SetWalletInstanceStatusHandler({ credentialRepository: pidIssuerClient, - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository: userRepositoryThatFailsOnGetUser, userTrialSubscriptionRepository, @@ -435,9 +435,9 @@ describe("SetWalletInstanceStatusHandler", () => { }; const handler = SetWalletInstanceStatusHandler({ credentialRepository: pidIssuerClientThatFailsOnRevoke, - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -477,9 +477,9 @@ describe("SetWalletInstanceStatusHandler", () => { }; const handler = SetWalletInstanceStatusHandler({ credentialRepository: pidIssuerClient, - hslJwtValidate, input: req, inputDecoder: H.HttpRequest, + jwtValidate, logger, userRepository, userTrialSubscriptionRepository, @@ -497,8 +497,8 @@ describe("SetWalletInstanceStatusHandler", () => { }); }); - it("should return a 500 HTTP response on hslJwtValidate error", async () => { - const hslJwtValidateThatFails: HslJwtValidate = () => + it("should return a 500 HTTP response on jwtValidate error", async () => { + const jwtValidateThatFails: JwtValidate = () => TE.left(new Error("failed on jwtValidationAndDecode!")); const req = { @@ -514,9 +514,9 @@ describe("SetWalletInstanceStatusHandler", () => { }; const handler = SetWalletInstanceStatusHandler({ credentialRepository: pidIssuerClient, - hslJwtValidate: hslJwtValidateThatFails, input: req, inputDecoder: H.HttpRequest, + jwtValidate: jwtValidateThatFails, logger, userRepository, userTrialSubscriptionRepository, diff --git a/apps/io-wallet-user-func/src/infra/http/jwt-validator.ts b/apps/io-wallet-user-func/src/infra/http/jwt-validator.ts index 9cc27f7c..e1cf93bf 100644 --- a/apps/io-wallet-user-func/src/infra/http/jwt-validator.ts +++ b/apps/io-wallet-user-func/src/infra/http/jwt-validator.ts @@ -1,4 +1,4 @@ -import { HslJwtEnvironment, hslJwtValidate } from "@/jwt-validator"; +import { JwtEnvironment, jwtValidate } from "@/jwt-validator"; import * as H from "@pagopa/handler-kit"; import { FiscalCode, @@ -50,10 +50,10 @@ const requireFiscalCode: ( export const requireFiscalCodeFromToken: ( req: H.HttpRequest, -) => RTE.ReaderTaskEither = flow( +) => RTE.ReaderTaskEither = flow( requireAuthorizationHeader, E.chainW(requireBearerToken), RTE.fromEither, - RTE.chain(hslJwtValidate), + RTE.chain(jwtValidate), RTE.chainW(flow(requireFiscalCode, RTE.fromEither)), ); diff --git a/apps/io-wallet-user-func/src/infra/http/whitelisted-user.ts b/apps/io-wallet-user-func/src/infra/http/whitelisted-user.ts index 9f2651c1..7d94fa7e 100644 --- a/apps/io-wallet-user-func/src/infra/http/whitelisted-user.ts +++ b/apps/io-wallet-user-func/src/infra/http/whitelisted-user.ts @@ -1,4 +1,4 @@ -import { HslJwtEnvironment } from "@/jwt-validator"; +import { JwtEnvironment } from "@/jwt-validator"; import { User, UserEnvironment, @@ -16,7 +16,7 @@ import { requireFiscalCodeFromToken } from "./jwt-validator"; export const requireWhitelistedFiscalCodeFromToken: ( req: H.HttpRequest, ) => RTE.ReaderTaskEither< - HslJwtEnvironment & UserTrialSubscriptionEnvironment, + JwtEnvironment & UserTrialSubscriptionEnvironment, Error, FiscalCode > = flow(requireFiscalCodeFromToken, RTE.chainFirstW(ensureUserInWhitelist)); @@ -24,7 +24,7 @@ export const requireWhitelistedFiscalCodeFromToken: ( export const requireWhitelistedUserFromToken: ( req: H.HttpRequest, ) => RTE.ReaderTaskEither< - HslJwtEnvironment & UserEnvironment & UserTrialSubscriptionEnvironment, + JwtEnvironment & UserEnvironment & UserTrialSubscriptionEnvironment, Error, User > = flow( diff --git a/apps/io-wallet-user-func/src/infra/jwt-validator.ts b/apps/io-wallet-user-func/src/infra/jwt-validator.ts index 88006283..02bd9e44 100644 --- a/apps/io-wallet-user-func/src/infra/jwt-validator.ts +++ b/apps/io-wallet-user-func/src/infra/jwt-validator.ts @@ -1,7 +1,7 @@ /* eslint-disable @typescript-eslint/no-unused-vars */ -import { HubSpidLoginConfig } from "@/app/config"; +import { JwtValidatorConfig } from "@/app/config"; import { UnauthorizedError } from "@/error"; -import { ExchangeJwtValidate, HslJwtValidate } from "@/jwt-validator"; +import { JwtValidate } from "@/jwt-validator"; import { getValidateJWT } from "@pagopa/ts-commons/lib/jwt_with_key_rotation"; import { NonEmptyString } from "@pagopa/ts-commons/lib/strings"; import * as E from "fp-ts/lib/Either"; @@ -58,11 +58,11 @@ const hslIntrospection: ( ), ); -export const hslValidate: ({ +const hslJwtValidate: ({ clientBaseUrl, jwtIssuer, jwtPubKey, -}: HubSpidLoginConfig) => HslJwtValidate = +}: JwtValidatorConfig["hubSpidLogin"]) => JwtValidate = ({ clientBaseUrl, jwtIssuer, jwtPubKey }) => (token) => pipe( @@ -70,14 +70,29 @@ export const hslValidate: ({ validateAndDecode(jwtIssuer, jwtPubKey), // TODO [SIW-1327]: make the call to hub spid login service // TE.chainFirst(() => pipe(token, hslIntrospection(clientBaseUrl))), - TE.mapLeft(() => new UnauthorizedError()), ); -const exchangeValidate: ({ - clientBaseUrl, +const exchangeJwtValidate: ({ jwtIssuer, jwtPubKey, -}: HubSpidLoginConfig) => ExchangeJwtValidate = +}: JwtValidatorConfig["exchange"]) => JwtValidate = ({ jwtIssuer, jwtPubKey }) => (token) => pipe(token, validateAndDecode(jwtIssuer, jwtPubKey)); + +// there are two types of tokens +// one issued by the hub SPID login when the user logs in directly (validated with hslJwtValidate) +// and another issued when the user enters via a magic link (validated with exchangeJwtValidate) +// this function returns 'Right' if at least one of these validations returns 'Right'. +export const jwtValidate: ({ + exchange, + hubSpidLogin, +}: JwtValidatorConfig) => JwtValidate = + ({ exchange, hubSpidLogin }) => + (token) => + pipe( + token, + hslJwtValidate(hubSpidLogin), + TE.orElse(() => pipe(token, exchangeJwtValidate(exchange))), + TE.mapLeft(() => new UnauthorizedError()), + ); diff --git a/apps/io-wallet-user-func/src/jwt-validator.ts b/apps/io-wallet-user-func/src/jwt-validator.ts index bcab33f0..43b8a221 100644 --- a/apps/io-wallet-user-func/src/jwt-validator.ts +++ b/apps/io-wallet-user-func/src/jwt-validator.ts @@ -3,33 +3,17 @@ import * as RTE from "fp-ts/lib/ReaderTaskEither"; import * as TE from "fp-ts/lib/TaskEither"; import * as jwt from "jsonwebtoken"; -export type HslJwtValidate = ( +export type JwtValidate = ( token: NonEmptyString, ) => TE.TaskEither; -export interface HslJwtEnvironment { - hslJwtValidate: HslJwtValidate; +export interface JwtEnvironment { + jwtValidate: JwtValidate; } -export const hslJwtValidate: ( +export const jwtValidate: ( token: NonEmptyString, -) => RTE.ReaderTaskEither = +) => RTE.ReaderTaskEither = (token) => - ({ hslJwtValidate }) => - hslJwtValidate(token); - -export type ExchangeJwtValidate = ( - token: NonEmptyString, -) => TE.TaskEither; - -interface ExchangeJwtEnvironment { - exchangeJwtValidate: ExchangeJwtValidate; -} - -// eslint-disable-next-line @typescript-eslint/no-unused-vars -const exchangeJwtValidate: ( - token: NonEmptyString, -) => RTE.ReaderTaskEither = - (token) => - ({ exchangeJwtValidate }) => - exchangeJwtValidate(token); + ({ jwtValidate }) => + jwtValidate(token); diff --git a/infra/resources/_modules/function_apps/locals.tf b/infra/resources/_modules/function_apps/locals.tf index 82008b04..5c55b2b8 100644 --- a/infra/resources/_modules/function_apps/locals.tf +++ b/infra/resources/_modules/function_apps/locals.tf @@ -41,6 +41,8 @@ locals { HubSpidLoginJwtPubKey = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUE0TXZaQmMxSFhxbDFPdkZYZnRLcGhkV1pCalRWc0dhWmcvYURhYmt2SE9pdElaYWQrTVJXV3BQSyt1WEVkdjRNN0hjNVFaRVpwOCtoREhLVHZ0TklkWEx0bWpFSCtTV3dYaDdaZU1vUXJqL1RONmVJNTA2TDM5bEdjRnNTWmZtVFlyQVpwcFNRMzM4SWhiWjdjdUtJT0Y3QU4wOUc4VXp2MkUxTGdXSGQzK0UzRldJaWhKckhFQlJpUGY5MGRYVUI4QzR0MmRCZE4xbm15R1lidlJ3bFRpR2UzWkJpQytETVdaQjFZZFFOYUpwb2N3L0orMERPNnBJSmJjWitHSGZnbVpXOG5xOCtBZ3RtOWFRY09UajErYTIydHBrTUM2QzVNaVoxUERuc3R3Qnpmekp2NnVyTkZNUytwNXZWUWpCbTExUHBqZzlDV3ZXcWNtOUFJL21hQXdJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t", HubSpidLoginJwtIssuer = "api-web.io.pagopa.it/ioweb/auth" HubSpidLoginClientBaseUrl = "https://io-p-weu-ioweb-spid-login.azurewebsites.net" + ExchangeJwtIssuer = "api-web.io.pagopa.it/ioweb/backend" + ExchangeJwtPubKey = "LS0tLS1CRUdJTiBQVUJMSUMgS0VZLS0tLS0KTUlJQklqQU5CZ2txaGtpRzl3MEJBUUVGQUFPQ0FROEFNSUlCQ2dLQ0FRRUFyNjlzOGVrRWRaSER2eTdZR1hnSEVPZUMvNEVpTVRJMDdYcUh6YjFQZi9IeDNTRW9lZkl3RXplRHcvSVZmd0FDUVMxdm5VeEJRemNPdXlGY1R3ZU9raFZ2NERVMmdLU1FIaC9kcklFZVBjZlgwcjBJYWNpeXZPVDE1TzdNWHQ2VE1FOWhTY2llM0cxT0pZRXlOZUFJVlBnUjN0S1ZTWEU5Wkk0VmthQzc4bFlrUVV4SDFaRkFUMk5ZNWNzZGo5Wi9sOTBCUHpMSzF0OFlqdDV5Z2w2UzkxQlNoMmk2QW5tZ041MUZxNXRZMTlJOTFuR09TSUpmRnVhY1hmbjZTRytHbUp3QWxSRHl3TUVXNU9mV0NYS3didDBFQkFsRWRoeitRRXdSYkg1WXVNRnR3VThhUnNobTgzRFI0VVI0dmV6cXlOOVpuQmM1cEtjYzkzTjQzMVRCTXdJREFRQUIKLS0tLS1FTkQgUFVCTElDIEtFWS0tLS0t" TrialSystemApiBaseURL = "https://api.trial.pagopa.it" TrialSystemTrialId = "01J2GN4TA8FB6DPTAX3T3YD6M1"