diff --git a/src/utils/__tests__/saml.test.ts b/src/utils/__tests__/saml.test.ts
index 5926171c..0432b976 100644
--- a/src/utils/__tests__/saml.test.ts
+++ b/src/utils/__tests__/saml.test.ts
@@ -2,6 +2,7 @@ import { right } from "fp-ts/lib/Either";
import { isSome, tryCatch } from "fp-ts/lib/Option";
import { fromEither } from "fp-ts/lib/TaskEither";
import { SamlConfig } from "passport-saml";
+import { Builder } from "xml2js";
import { DOMParser } from "xmldom";
import { EventTracker } from "../../index";
import {
@@ -12,7 +13,11 @@ import {
samlResponseCIE
} from "../__mocks__/saml";
import { StrictResponseValidationOptions } from "../middleware";
-import { getPreValidateResponse, getXmlFromSamlResponse } from "../saml";
+import {
+ getAuthorizeRequestTamperer,
+ getPreValidateResponse,
+ getXmlFromSamlResponse
+} from "../saml";
import * as saml from "../saml";
const samlConfig: SamlConfig = ({
@@ -54,6 +59,43 @@ describe("getXmlFromSamlResponse", () => {
});
});
+describe("getAuthorizeRequestTamperer", () => {
+ it("should add professional spid extension", async () => {
+ await getAuthorizeRequestTamperer(
+ // spid-testenv does not accept an xml header with utf8 encoding
+ new Builder({ xmldec: { encoding: undefined, version: "1.0" } }),
+ {
+ professionalSpidExtension: {
+ professionalSpidEnabled: true,
+ purpose: "P"
+ }
+ // tslint:disable-next-line: no-any
+ } as any,
+ samlConfig
+ )(samlRequest)
+ .fold(
+ err => fail(err),
+ _ => expect(_).toContain("P")
+ )
+ .run();
+ });
+
+ it("should not add professional spid extension", async () => {
+ await getAuthorizeRequestTamperer(
+ // spid-testenv does not accept an xml header with utf8 encoding
+ new Builder({ xmldec: { encoding: undefined, version: "1.0" } }),
+ // tslint:disable-next-line: no-any
+ {} as any,
+ samlConfig
+ )(samlRequest)
+ .fold(
+ err => fail(err),
+ _ => expect(_).not.toContain(" {
const mockCallback = jest.fn();
@@ -80,7 +122,7 @@ describe("preValidateResponse", () => {
error
? expect(callback).toBeCalledWith(error)
: expect(callback).toBeCalledWith(null, true, expect.any(String));
- resolve();
+ resolve(void 0);
}, 100);
});
diff --git a/src/utils/middleware.ts b/src/utils/middleware.ts
index b4b5ad48..aa40f9cb 100644
--- a/src/utils/middleware.ts
+++ b/src/utils/middleware.ts
@@ -4,6 +4,7 @@
import * as express from "express";
import { array } from "fp-ts/lib/Array";
import { Task, task } from "fp-ts/lib/Task";
+import * as t from "io-ts";
import { NonEmptyString } from "italia-ts-commons/lib/strings";
import { Profile, SamlConfig, VerifiedCallback } from "passport-saml";
import { RedisClient } from "redis";
@@ -18,6 +19,25 @@ import { IDPEntityDescriptor } from "../types/IDPEntityDescriptor";
import { fetchIdpsMetadata } from "./metadata";
import { logSamlCertExpiration, SamlAttributeT } from "./saml";
+export const ProfessionalPurpose = t.keyof({
+ LP: null,
+ P: null,
+ PF: null,
+ PG: null,
+ PX: null
+});
+
+const ProfessionalSpidExtension = t.union([
+ t.interface({
+ professionalSpidEnabled: t.literal(false)
+ }),
+ t.interface({
+ professionalSpidEnabled: t.literal(true),
+ purpose: ProfessionalPurpose
+ })
+]);
+
+type ProfessionalSpidExtension = t.TypeOf;
interface IServiceProviderOrganization {
URL: string;
displayName: string;
@@ -34,6 +54,7 @@ export interface IServiceProviderConfig {
IDPMetadataUrl: string;
organization: IServiceProviderOrganization;
publicCert: string;
+ professionalSpidExtension?: ProfessionalSpidExtension;
strictResponseValidation?: StrictResponseValidationOptions;
}
diff --git a/src/utils/saml.ts b/src/utils/saml.ts
index 551b465c..43a79892 100644
--- a/src/utils/saml.ts
+++ b/src/utils/saml.ts
@@ -68,6 +68,8 @@ export const SAML_NAMESPACE = {
XMLDSIG: "http://www.w3.org/2000/09/xmldsig#"
};
+const SPID_SAML_EXTENSION = "https://spid.gov.it/samlâextensions";
+
const ISSUER_FORMAT = "urn:oasis:names:tc:SAML:2.0:nameid-format:entity";
const decodeBase64 = (s: string) => Buffer.from(s, "base64").toString("utf8");
@@ -496,6 +498,17 @@ export const getAuthorizeRequestTamperer = (
authnRequest["saml:Issuer"][0].$.NameQualifier = samlConfig.issuer;
// tslint:disable-next-line: no-object-mutation
authnRequest["saml:Issuer"][0].$.Format = ISSUER_FORMAT;
+ if (_.professionalSpidExtension?.professionalSpidEnabled === true) {
+ // tslint:disable-next-line: no-object-mutation
+ authnRequest["samlp:Extensions"] = {
+ $: {
+ "xmlns:spid": SPID_SAML_EXTENSION
+ },
+ "spid:Purpose": {
+ _: _.professionalSpidExtension.purpose
+ }
+ };
+ }
return o;
}, toError)
)