Skip to content

Commit

Permalink
fix: added magic link jwt validation
Browse files Browse the repository at this point in the history
Refs: #196 SIW-1481
  • Loading branch information
silvicir authored Sep 4, 2024
1 parent 5a14820 commit 5ed07a0
Show file tree
Hide file tree
Showing 11 changed files with 134 additions and 130 deletions.
5 changes: 5 additions & 0 deletions .changeset/unlucky-wolves-accept.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"io-wallet-user-func": patch
---

Add io-web magic link jwt validation
2 changes: 2 additions & 0 deletions apps/io-wallet-user-func/local.settings.json.example
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
100 changes: 48 additions & 52 deletions apps/io-wallet-user-func/src/app/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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<typeof HubSpidLoginConfig>;
export type JwtValidatorConfig = t.TypeOf<typeof JwtValidatorConfig>;

const TrialSystemApiClientConfig = t.type({
apiKey: t.string,
Expand Down Expand Up @@ -116,54 +122,14 @@ export const Config = t.type({
azure: AzureConfiguration,
crypto: CryptoConfiguration,
federationEntity: FederationEntityMetadata,
hubSpidLogin: HubSpidLoginConfig,
jwtValidator: JwtValidatorConfig,
pdvTokenizer: PdvTokenizerApiClientConfig,
pidIssuer: PidIssuerApiClientConfig,
trialSystem: TrialSystemApiClientConfig,
});

export type Config = t.TypeOf<typeof Config>;

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,
Expand Down Expand Up @@ -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(
Expand All @@ -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))),
),
Expand Down Expand Up @@ -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,
}),
);
8 changes: 4 additions & 4 deletions apps/io-wallet-user-func/src/app/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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";
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -130,7 +130,7 @@ app.timer("generateEntityConfiguration", {
app.http("getCurrentWalletInstanceStatus", {
authLevel: "function",
handler: GetCurrentWalletInstanceStatusFunction({
hslJwtValidate,
jwtValidate: tokenValidate,
userRepository: pdvTokenizerClient,
userTrialSubscriptionRepository: trialSystemClient,
walletInstanceRepository,
Expand All @@ -143,7 +143,7 @@ app.http("setWalletInstanceStatus", {
authLevel: "function",
handler: SetWalletInstanceStatusFunction({
credentialRepository: pidIssuerClient,
hslJwtValidate,
jwtValidate: tokenValidate,
userRepository: pdvTokenizerClient,
userTrialSubscriptionRepository: trialSystemClient,
walletInstanceRepository,
Expand Down
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -51,7 +51,7 @@ describe("GetCurrentWalletInstanceStatusHandler", () => {
TE.right({ id: "pdv_id" as NonEmptyString }),
};

const hslJwtValidate: HslJwtValidate = () =>
const jwtValidate: JwtValidate = () =>
TE.right({
fiscal_number: "AAACCC94D55H501P",
});
Expand All @@ -72,9 +72,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => {
},
};
const handler = GetCurrentWalletInstanceStatusHandler({
hslJwtValidate,
input: req,
inputDecoder: H.HttpRequest,
jwtValidate,
logger,
userRepository,
userTrialSubscriptionRepository,
Expand All @@ -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,
Expand Down Expand Up @@ -134,9 +134,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => {
},
};
const handler = GetCurrentWalletInstanceStatusHandler({
hslJwtValidate,
input: req,
inputDecoder: H.HttpRequest,
jwtValidate,
logger,
userRepository,
userTrialSubscriptionRepository,
Expand Down Expand Up @@ -167,9 +167,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => {
},
};
const handler = GetCurrentWalletInstanceStatusHandler({
hslJwtValidate,
input: req,
inputDecoder: H.HttpRequest,
jwtValidate,
logger,
userRepository,
userTrialSubscriptionRepository,
Expand All @@ -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",
});
Expand All @@ -205,9 +205,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => {
},
};
const handler = GetCurrentWalletInstanceStatusHandler({
hslJwtValidate,
input: req,
inputDecoder: H.HttpRequest,
jwtValidate,
logger,
userRepository,
userTrialSubscriptionRepository,
Expand All @@ -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"),
Expand All @@ -240,9 +240,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => {
},
};
const handler = GetCurrentWalletInstanceStatusHandler({
hslJwtValidate: hslJwtValidateThatFails,
input: req,
inputDecoder: H.HttpRequest,
jwtValidate: jwtValidateThatFails,
logger,
userRepository,
userTrialSubscriptionRepository,
Expand Down Expand Up @@ -281,9 +281,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => {
},
};
const handler = GetCurrentWalletInstanceStatusHandler({
hslJwtValidate,
input: req,
inputDecoder: H.HttpRequest,
jwtValidate,
logger,
userRepository,
userTrialSubscriptionRepository:
Expand Down Expand Up @@ -317,9 +317,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => {
},
};
const handler = GetCurrentWalletInstanceStatusHandler({
hslJwtValidate,
input: req,
inputDecoder: H.HttpRequest,
jwtValidate,
logger,
userRepository,
userTrialSubscriptionRepository,
Expand Down Expand Up @@ -353,9 +353,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => {
},
};
const handler = GetCurrentWalletInstanceStatusHandler({
hslJwtValidate,
input: req,
inputDecoder: H.HttpRequest,
jwtValidate,
logger,
userRepository,
userTrialSubscriptionRepository,
Expand All @@ -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 = {
Expand All @@ -385,9 +385,9 @@ describe("GetCurrentWalletInstanceStatusHandler", () => {
},
};
const handler = GetCurrentWalletInstanceStatusHandler({
hslJwtValidate: hslValidateThatFails,
input: req,
inputDecoder: H.HttpRequest,
jwtValidate: jwtValidateThatFails,
logger,
userRepository,
userTrialSubscriptionRepository,
Expand Down
Loading

0 comments on commit 5ed07a0

Please sign in to comment.