{"action":"created","issue":{"url":"https://api.github.com/repos/tngan/samlify/issues/222","repository_url":"https://api.github.com/repos/tngan/samlify","labels_url":"https://api.github.com/repos/tngan/samlify/issues/222/labels{/name}","comments_url":"https://api.github.com/repos/tngan/samlify/issues/222/comments","events_url":"https://api.github.com/repos/tngan/samlify/issues/222/events","html_url":"https://github.com/tngan/samlify/issues/222","id":371448938,"node_id":"MDU6SXNzdWUzNzE0NDg5Mzg=","number":222,"title":"Using parseLoginResponse leads to typeError","user":{"login":"laimikko1","id":19820360,"node_id":"MDQ6VXNlcjE5ODIwMzYw","avatar_url":"https://avatars0.githubusercontent.com/u/19820360?v=4","gravatar_id":"","url":"https://api.github.com/users/laimikko1","html_url":"https://github.com/laimikko1","followers_url":"https://api.github.com/users/laimikko1/followers","following_url":"https://api.github.com/users/laimikko1/following{/other_user}","gists_url":"https://api.github.com/users/laimikko1/gists{/gist_id}","starred_url":"https://api.github.com/users/laimikko1/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/laimikko1/subscriptions","organizations_url":"https://api.github.com/users/laimikko1/orgs","repos_url":"https://api.github.com/users/laimikko1/repos","events_url":"https://api.github.com/users/laimikko1/events{/privacy}","received_events_url":"https://api.github.com/users/laimikko1/received_events","type":"User","site_admin":false},"labels":[{"id":617745723,"node_id":"MDU6TGFiZWw2MTc3NDU3MjM=","url":"https://api.github.com/repos/tngan/samlify/labels/discussion","name":"discussion","color":"5319e7","default":false},{"id":247751121,"node_id":"MDU6TGFiZWwyNDc3NTExMjE=","url":"https://api.github.com/repos/tngan/samlify/labels/enhancement","name":"enhancement","color":"84b6eb","default":true}],"state":"open","locked":false,"assignee":null,"assignees":[],"milestone":null,"comments":2,"created_at":"2018-10-18T09:51:25Z","updated_at":"2018-10-23T11:34:58Z","closed_at":null,"author_association":"NONE","body":"We are using Samlify as an SP and authenticating using a 3rd party idp solution.\r\n\r\nAt first I tried to set up our SP and idp as advised in the documentation\r\n\r\n```\r\nconst sp = ServiceProvider({\r\n  metadata: readFileSync('./our_sp_metada.xml'),\r\n});\r\n```\r\nand idp using the metadata, returned to us by the WAYF/DS service that our idp provider uses\r\n\r\n```\r\nconst samlify = require('samlify')\r\n...\r\nconst metadata = await getMetadata(req.query.entityID)\r\nconst idp = IdentityProvider({\r\n  metadata\r\n});\r\n\r\n```\r\nThis worked pretty far, as I was able to redirect our login request from our 3rd party WAYF/DS to the test idp login service, managed to authenticate and received a SAML response.\r\nHowever, when trying to parse the login response using\r\n\r\n```\r\nrouter.post('/assert', async (req: Request, res: Response): Promise<void> => {\r\n  try {\r\n    const response = await sp.parseLoginResponse(idp, 'post', req)\r\n    console.log(response)\r\n  } catch (error) {\r\n    console.log(error)\r\n  } \r\n```\r\nI catched the following error\r\n\r\n```\r\nbackend | TypeError: Cannot read property 'map' of null\r\nbackend |     at /usr/src/app/node_modules/samlify/build/src/libsaml.js:304:49\r\nbackend |     at Array.forEach (<anonymous>)\r\nbackend |     at Object.verifySignature (/usr/src/app/node_modules/samlify/build/src/libsaml.js:289:23)\r\nbackend |     at /usr/src/app/node_modules/samlify/build/src/flow.js:169:48\r\nbackend |     at step (/usr/src/app/node_modules/samlify/build/src/flow.js:32:23)\r\nbackend |     at Object.next (/usr/src/app/node_modules/samlify/build/src/flow.js:13:53)\r\nbackend |     at fulfilled (/usr/src/app/node_modules/samlify/build/src/flow.js:4:58)\r\nbackend |     at <anonymous>\r\nbackend |     at process._tickCallback (internal/process/next_tick.js:189:7)\r\n```\r\n\r\nAfter a bit of head scratching I realised that the assertion of the response is probably both signed and encrypted. I then followed the Samlify documentation and modified my sp\r\n\r\n```\r\nconst sp = samlify.ServiceProvider({\r\n  metadata: fs.readFileSync('./our_metadata/metadata.xml'),\r\n  encPrivateKey: fs.readFileSync('./our_cert_key.pem'),\r\n  privateKey: fs.readFileSync('./our_cert_key.pem')\r\n})\r\n```\r\nAnd added \r\n```\r\n<SPSSODescriptor \r\n    WantAssertionsSigned=\"true\" \r\n    protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\r\n```\r\nto my SP metadata.\r\n(We are using the same certificate for everything in our testing environment.)\r\n\r\nI then tried to parse the login response but received the same error as above.\r\n\r\nHere's our idp and SP logged: \r\n\r\n```\r\nIdentityProvider {\r\n  entitySetting: \r\n   { wantLogoutResponseSigned: false,\r\n     messageSigningOrder: 'sign-then-encrypt',\r\n     wantLogoutRequestSigned: false,\r\n     allowCreate: false,\r\n     isAssertionEncrypted: false,\r\n     requestSignatureAlgorithm: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',\r\n     dataEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',\r\n     keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-1_5',\r\n     generateID: [Function: generateID],\r\n     relayState: '',\r\n     wantAuthnRequestsSigned: false,\r\n     tagPrefix: { encryptedAssertion: 'saml' },\r\n     metadata: '<?xml version=\"...\"</EntityDescriptor>' },\r\n  entityMeta: \r\n   IdpMetadata {\r\n     xmlString: '<?xml version=\"1.0\" encoding=\"UTF-8\" ...</EntityDescriptor>',\r\n     meta: \r\n      { wantAuthnRequestsSigned: undefined,\r\n        singleSignOnService: [Object],\r\n        entityDescriptor: '<EntityDescriptor .... </EntityDescriptor>',\r\n        entityID: '[entityID of our idp]',\r\n        certificate: {},\r\n        singleLogoutService: [Object],\r\n        nameIDFormat: [Array] } } }\r\n```\r\nWhat stands out is the empty certificate in meta information.\r\n\r\n```\r\nServiceProvider {\r\n  entitySetting: \r\n   { wantLogoutResponseSigned: false,\r\n     messageSigningOrder: 'sign-then-encrypt',\r\n     wantLogoutRequestSigned: false,\r\n     allowCreate: false,\r\n     isAssertionEncrypted: false,\r\n     requestSignatureAlgorithm: 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256',\r\n     dataEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#aes256-cbc',\r\n     keyEncryptionAlgorithm: 'http://www.w3.org/2001/04/xmlenc#rsa-1_5',\r\n     generateID: [Function: generateID],\r\n     relayState: [frontend login page url],\r\n     authnRequestsSigned: false,\r\n     wantAssertionsSigned: true,\r\n     wantMessageSigned: false,\r\n     metadata: <Buffer ... >,\r\n     encPrivateKey: <Buffer ... >,\r\n     privateKey: <Buffer  ... > },\r\n  entityMeta: \r\n   SpMetadata {\r\n     xmlString: '<?xml version=\"1.0\" ...</EntityDescriptor>\\n',\r\n     meta: \r\n      { spSSODescriptor: [Object],\r\n        assertionConsumerService: [Object],\r\n        entityDescriptor: '<EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" ...</EntityDescriptor>',\r\n        entityID: '[our_SP_entityID]',\r\n        certificate: [Object],\r\n        singleLogoutService: [],\r\n        nameIDFormat: 'urn:oasis:names:tc:SAML:2.0:nameid-format:transient' } } }\r\n```\r\n\r\nOur test idp metadata is public and found at\r\nhttps://testidp.funet.fi/idp/shibboleth\r\n\r\nOur SP metadata is published at\r\nhttps://defa-staging.cs.helsinki.fi/api/login/metadata\r\n\r\nIf you require other information, feel free to ask and I'll try to provide it.  Thanks!"},"comment":{"url":"https://api.github.com/repos/tngan/samlify/issues/comments/432210380","html_url":"https://github.com/tngan/samlify/issues/222#issuecomment-432210380","issue_url":"https://api.github.com/repos/tngan/samlify/issues/222","id":432210380,"node_id":"MDEyOklzc3VlQ29tbWVudDQzMjIxMDM4MA==","user":{"login":"laimikko1","id":19820360,"node_id":"MDQ6VXNlcjE5ODIwMzYw","avatar_url":"https://avatars0.githubusercontent.com/u/19820360?v=4","gravatar_id":"","url":"https://api.github.com/users/laimikko1","html_url":"https://github.com/laimikko1","followers_url":"https://api.github.com/users/laimikko1/followers","following_url":"https://api.github.com/users/laimikko1/following{/other_user}","gists_url":"https://api.github.com/users/laimikko1/gists{/gist_id}","starred_url":"https://api.github.com/users/laimikko1/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/laimikko1/subscriptions","organizations_url":"https://api.github.com/users/laimikko1/orgs","repos_url":"https://api.github.com/users/laimikko1/repos","events_url":"https://api.github.com/users/laimikko1/events{/privacy}","received_events_url":"https://api.github.com/users/laimikko1/received_events","type":"User","site_admin":false},"created_at":"2018-10-23T11:34:58Z","updated_at":"2018-10-23T11:34:58Z","author_association":"NONE","body":"Thanks for the response and patch.\r\nThere were few errors on my end that caused issues.\r\n\r\n1) The nameIDPolicy wasn't correctly configured, so the SAML response was in error state due to it. I added the following to SP\r\n\r\n```\r\nconst sp = samlify.ServiceProvider({\r\n  metadata: fs.readFileSync('./src/utils/metadata.xml'),\r\n  encPrivateKey: fs.readFileSync('./src/utils/key.pem'),\r\n  privateKey: fs.readFileSync('./src/utils/key.pem'),\r\n  loginNameIDFormat: \"transient\",\r\n})\r\n```\r\nand it now returns a valid response.\r\n\r\n2) Our idp signs the response and assertion and also encrypts the assertion. I followed the documentation and added \r\n\r\n```\r\nidp = samlify.IdentityProvider({\r\n    metadata: fs.readFileSync('./src/utils/idp-metadata.xml'),\r\n    isAssertionEncrypted: true,\r\n    wantMessageSigned: true,\r\n    signatureConfig: {\r\n      prefix: 'ds',\r\n      location: {\r\n        reference: '/samlp:Response/saml:Issuer',\r\n        action: 'after'\r\n    }\r\n  }\r\n})\r\n```\r\nIdp also expects us to sign our auth request so I modified my SP metadata to match that.\r\nThe SP metadata is still public\r\nhttps://defa-staging.cs.helsinki.fi/api/login/metadata\r\n\r\nI'm still using the test idp metadata. I've modifed it a bit as you instructed. It can be seen below\r\n\r\n```\r\n<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?><EntityDescriptor xmlns=\"urn:oasis:names:tc:SAML:2.0:metadata\" xmlns:mdui=\"urn:oasis:names:tc:SAML:metadata:ui\" xmlns:saml=\"urn:oasis:names:tc:SAML:2.0:assertion\" xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" entityID=\"[entityID]\" validUntil=\"2018-10-22T04:50:20+03:00\" xsi:schemaLocation=\"urn:oasis:names:tc:SAML:2.0:metadata saml-schema-metadata-2.0.xsd urn:mace:shibboleth:metadata:1.0 shibboleth-metadata-1.0.xsd http://www.w3.org/2000/09/xmldsig# xmldsig-core-schema.xsd\"><ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\r\n<ds:SignedInfo>\r\n<ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\r\n<ds:SignatureMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\"/>\r\n<ds:Reference URI=\"\">\r\n<ds:Transforms>\r\n<ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/>\r\n<ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\r\n</ds:Transforms>\r\n<ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"/>\r\n<ds:DigestValue>QwoRLp4+XrbRpHtVDU3Jew/jupDFBgCnxv43KlryQmk=</ds:DigestValue>\r\n</ds:Reference>\r\n</ds:SignedInfo>\r\n<ds:SignatureValue>\r\n[JnKk...]\r\n</ds:SignatureValue>\r\n<ds:KeyInfo>\r\n<ds:KeyValue>\r\n<ds:RSAKeyValue>\r\n<ds:Modulus>\r\n[0uxae...]\r\n</ds:Modulus>\r\n<ds:Exponent>AQAB</ds:Exponent>\r\n</ds:RSAKeyValue>\r\n</ds:KeyValue>\r\n<ds:X509Data>\r\n<ds:X509Certificate>\r\n[...ilNkbg==]\r\n</ds:X509Certificate>\r\n</ds:X509Data>\r\n</ds:KeyInfo>\r\n</ds:Signature>\r\n  <IDPSSODescriptor xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\" WantAuthnRequestsSigned=\"true\" xmlns:shibmd=\"urn:mace:shibboleth:metadata:1.0\" protocolSupportEnumeration=\"urn:oasis:names:tc:SAML:2.0:protocol\">\r\n    <Extensions>\r\n      <shibmd:Scope regexp=\"false\">funet.fi</shibmd:Scope>\r\n      <shibmd:Scope regexp=\"false\">yliopisto.fi</shibmd:Scope>\r\n      <mdui:UIInfo>\r\n          ...\r\n      </mdui:UIInfo>\r\n    </Extensions>\r\n    <KeyDescriptor use=\"signing\">\r\n      <ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\r\n        <ds:X509Data>\r\n          <ds:X509Certificate>[...PZo0=]</ds:X509Certificate>\r\n        </ds:X509Data>\r\n      </ds:KeyInfo>\r\n    </KeyDescriptor>\r\n    <SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"[Location]\"/>\r\n    <SingleLogoutService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"[Location]\"/>\r\n    <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:transient</NameIDFormat>\r\n    <NameIDFormat>urn:oasis:names:tc:SAML:2.0:nameid-format:persistent</NameIDFormat>\r\n    <SingleSignOnService Binding=\"urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect\" Location=\"[Location]\"/>\r\n  </IDPSSODescriptor>\r\n  <Organization>\r\n  </Organization>\r\n  <ContactPerson contactType=\"technical\">\r\n  </ContactPerson>\r\n  <ContactPerson contactType=\"support\">\r\n  </ContactPerson>\r\n</EntityDescriptor>\r\n```\r\nAlso, logging my  idp and SP certificates, I can see that my SP uses, for both signing and encryption, the certificate outlined in metadata.\r\n\r\nsp\r\n```\r\n{ \r\n  signing: '...Blw==' ,\r\n  encryption: '...Blw=='\r\n}\r\n```\r\nAnd idp also uses the cert for signing that I outlined in the metadata.\r\n\r\n```\r\n{ signing: '...PZo0=' }\r\n```\r\nI tried to validate my SAML response using an online SAML response tool. I added my SP private key, the idp cert that's in use for signing the response and the correct assertion urls. etc, and it was a valid response.\r\n\r\nHowever, when trying to parse the response, I get \r\n\r\n```\r\nError: ERR_FAILED_TO_VERIFY_SIGNATURE\r\n    at /usr/src/app/node_modules/samlify/build/src/libsaml.js:323:27\r\n    at Array.forEach (<anonymous>)\r\n    at Object.verifySignature (/usr/src/app/node_modules/samlify/build/src/libsaml.js:289:23)\r\n    at /usr/src/app/node_modules/samlify/build/src/flow.js:169:48\r\n    at step (/usr/src/app/node_modules/samlify/build/src/flow.js:32:23)\r\n    at Object.next (/usr/src/app/node_modules/samlify/build/src/flow.js:13:53)\r\n    at fulfilled (/usr/src/app/node_modules/samlify/build/src/flow.js:4:58)\r\n    at <anonymous>\r\n    at process._tickCallback (internal/process/next_tick.js:189:7)\r\n```\r\nI'd expect the signature cert to be the same for both message and assertion signing for the idp.\r\n\r\nI also checked the SAML response and the signature cert matched the one outlined in my idp for signing and the encryption cert matched the one outlined in my SP.\r\n\r\nHere's my received SAML response. I've removed unnecessary data, hopefully it's still readable.\r\n\r\n```\r\n<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<saml2p:Response Destination=\"[our SP assertion url]\"\r\n    ID=\"..\" InResponseTo=\"..\"\r\n    IssueInstant=\"2018-10-23T10:50:04.021Z\" Version=\"2.0\"\r\n    xmlns:saml2p=\"urn:oasis:names:tc:SAML:2.0:protocol\">\r\n    <saml2:Issuer xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\">[ipd entityId]</saml2:Issuer>\r\n    <ds:Signature xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\r\n        <ds:SignedInfo>\r\n            <ds:CanonicalizationMethod Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\r\n            <ds:SignatureMethod Algorithm=\"http://www.w3.org/2001/04/xmldsig-more#rsa-sha256\"/>\r\n            <ds:Reference URI=\"...\">\r\n                <ds:Transforms>\r\n                    <ds:Transform Algorithm=\"http://www.w3.org/2000/09/xmldsig#enveloped-signature\"/>\r\n                    <ds:Transform Algorithm=\"http://www.w3.org/2001/10/xml-exc-c14n#\"/>\r\n                </ds:Transforms>\r\n                <ds:DigestMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#sha256\"/>\r\n                <ds:DigestValue>[digest value]</ds:DigestValue>\r\n            </ds:Reference>\r\n        </ds:SignedInfo>\r\n        <ds:SignatureValue>\r\n            [...value...]\r\n        </ds:SignatureValue>\r\n        <ds:KeyInfo>\r\n            <ds:X509Data>\r\n                <ds:X509Certificate>[...PZo0=]</ds:X509Certificate>\r\n            </ds:X509Data>\r\n        </ds:KeyInfo>\r\n    </ds:Signature>\r\n    <saml2p:Status><saml2p:StatusCode Value=\"urn:oasis:names:tc:SAML:2.0:status:Success\"/></saml2p:Status>\r\n    <saml2:EncryptedAssertion xmlns:saml2=\"urn:oasis:names:tc:SAML:2.0:assertion\">\r\n        <xenc:EncryptedData Id=\"...\"\r\n            Type=\"http://www.w3.org/2001/04/xmlenc#Element\" xmlns:xenc=\"http://www.w3.org/2001/04/xmlenc#\"><xenc:EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#aes128-cbc\"\r\n            xmlns:xenc=\"http://www.w3.org/2001/04/xmlenc#\"/>\r\n            <ds:KeyInfo xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\">\r\n                <xenc:EncryptedKey Id=\"...\"\r\n                    Recipient=\"[our entityId]\" xmlns:xenc=\"http://www.w3.org/2001/04/xmlenc#\">\r\n                    <xenc:EncryptionMethod Algorithm=\"http://www.w3.org/2001/04/xmlenc#rsa-oaep-mgf1p\"\r\n                        xmlns:xenc=\"http://www.w3.org/2001/04/xmlenc#\"><ds:DigestMethod Algorithm=\"http://www.w3.org/2000/09/xmldsig#sha1\"\r\n                        xmlns:ds=\"http://www.w3.org/2000/09/xmldsig#\"/></xenc:EncryptionMethod>\r\n                    <ds:KeyInfo>\r\n                        <ds:X509Data>\r\n                            <ds:X509Certificate>[...Blw==]</ds:X509Certificate>\r\n                        </ds:X509Data>\r\n                    </ds:KeyInfo>\r\n                    <xenc:CipherData xmlns:xenc=\"http://www.w3.org/2001/04/xmlenc#\">\r\n                        <xenc:CipherValue>[...]</xenc:CipherValue>\r\n                    </xenc:CipherData>\r\n                </xenc:EncryptedKey>\r\n            </ds:KeyInfo>\r\n            <xenc:CipherData xmlns:xenc=\"http://www.w3.org/2001/04/xmlenc#\">\r\n                <xenc:CipherValue>[...]</xenc:CipherValue>\r\n            </xenc:CipherData>\r\n        </xenc:EncryptedData>\r\n    </saml2:EncryptedAssertion>\r\n</saml2p:Response>\r\n```"}}
{"actor":{"display_login":"laimikko1"}}