diff --git a/src/saml.ts b/src/saml.ts index fa8de39a..4dbe9978 100644 --- a/src/saml.ts +++ b/src/saml.ts @@ -1167,18 +1167,33 @@ class SAML { }; if (attributes) { + const profileAttributes: Record = {}; + attributes.forEach((attribute) => { if (!Object.prototype.hasOwnProperty.call(attribute, "AttributeValue")) { // if attributes has no AttributeValue child, continue return; } - const value = attribute.AttributeValue; - if (value.length === 1) { - profile[attribute.$.Name] = attrValueMapper(value[0]); - } else { - profile[attribute.$.Name] = value.map(attrValueMapper); + + const name = attribute.$.Name; + const value = + attribute.AttributeValue.length === 1 + ? attrValueMapper(attribute.AttributeValue[0]) + : attribute.AttributeValue.map(attrValueMapper); + + profileAttributes[name] = value; + + // If any property is already present in profile and is also present + // in attributes, then skip the one from attributes. Handle this + // conflict gracefully without returning any error + if (Object.prototype.hasOwnProperty.call(profile, name)) { + return; } + + profile[name] = value; }); + + profile.attributes = profileAttributes; } } diff --git a/test/tests.spec.ts b/test/tests.spec.ts index bedac6f4..d0422bff 100644 --- a/test/tests.spec.ts +++ b/test/tests.spec.ts @@ -1895,10 +1895,13 @@ describe("node-saml /", function () { }); }); describe("validatePostRequest()", function () { + const signingKey: any = fs.readFileSync(__dirname + "/static/key.pem", "ascii"); + const signingCert: any = fs.readFileSync(__dirname + "/static/cert.pem", "ascii"); let samlObj: SAML; + beforeEach(function () { samlObj = new SAML({ - cert: fs.readFileSync(__dirname + "/static/cert.pem", "ascii"), + cert: signingCert, }); }); @@ -1972,7 +1975,35 @@ describe("node-saml /", function () { sessionIndex: "1", }); }); + + it("check conflicting profile fields with data from attributes", async () => { + const testSAMLObj = new SAML({ cert: signingCert, issuer: "okta" }); + const xml = + '' + + '' + + "http://idp.example.com/metadata.php" + + "" + + '' + + '' + + "" + + '' + + 'test' + + "" + + "" + + "" + + ""; + const signedXml = signXmlResponse(xml, { privateKey: signingKey }); + const { profile } = await testSAMLObj.validatePostResponseAsync({ + SAMLResponse: Buffer.from(signedXml).toString("base64"), + }); + + should(profile!.issuer).not.be.equal("test"); + }); }); + it("validatePostRequest errors for encrypted nameID with wrong decryptionPvk", async () => { const samlObj = new SAML({ cert: fs.readFileSync(__dirname + "/static/cert.pem", "ascii"),