diff --git a/src/libsaml.ts b/src/libsaml.ts index 3ea5231..0ba1108 100644 --- a/src/libsaml.ts +++ b/src/libsaml.ts @@ -263,11 +263,13 @@ const libSaml = () => { * @param {array} tagValues tag values * @return {string} */ - replaceTagsByValue(rawXML: string, tagValues: any): string { + replaceTagsByValue(rawXML: string, tagValues: Record): string { Object.keys(tagValues).forEach(t => { + let tagValue = tagValues[t]; + tagValue = tagValue == null ? tagValue : tagValue.toString(); rawXML = rawXML.replace( new RegExp(`("?)\\{${t}\\}`, 'g'), - escapeTag(tagValues[t]) + escapeTag(tagValue as string) ); }); return rawXML; @@ -453,7 +455,7 @@ const libSaml = () => { sig.loadSignature(signatureNode); - doc.removeChild(signatureNode); + signatureNode.parentNode.removeChild(signatureNode); verified = verified && sig.checkSignature(doc.toString()); diff --git a/test/flow.ts b/test/flow.ts index 4ff6175..8ea4def 100644 --- a/test/flow.ts +++ b/test/flow.ts @@ -31,6 +31,8 @@ const { const binding = ref.namespace.binding; +const xmlProlog = ''; + // Custom template const loginResponseTemplate = { context: '{Issuer}{Issuer}{NameID}{Audience}{AttributeStatement}', @@ -146,6 +148,14 @@ const idp = identityProvider(defaultIdpConfig); const sp = serviceProvider(defaultSpConfig); const idpNoEncrypt = identityProvider({ ...defaultIdpConfig, isAssertionEncrypted: false }); const idpcustomNoEncrypt = identityProvider({ ...defaultIdpConfig, isAssertionEncrypted: false, loginResponseTemplate }); +const idpcustomPrologNoEncrypt = identityProvider({ + ...defaultIdpConfig, + isAssertionEncrypted: false, + loginResponseTemplate: { + ...loginResponseTemplate, + context: xmlProlog + loginResponseTemplate.context, + } +}); const idpcustom = identityProvider({ ...defaultIdpConfig, loginResponseTemplate }); const idpEncryptThenSign = identityProvider({ ...defaultIdpConfig, messageSigningOrder: 'encrypt-then-sign' }); const spWantLogoutReqSign = serviceProvider({ ...defaultSpConfig, wantLogoutRequestSigned: true }); @@ -155,7 +165,7 @@ const spNoAssertSignCustomConfig = serviceProvider({ ...defaultSpConfig, metadata: spmetaNoAssertSign, signatureConfig: { prefix: 'ds', - location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Issuer']", action: 'after' }, + location: { reference: "/*[local-name(.)='Response']", action: 'prepend' }, }, }); const spWithClockDrift = serviceProvider({ ...defaultSpConfig, clockDrifts: [-2000, 2000] }); @@ -408,6 +418,28 @@ test('send response with signed assertion and parse it', async t => { t.is(extract.response.inResponseTo, 'request_id'); }); +// + XML prolog +test('send response with [prolog + custom template] signed assertion and parse it', async t => { + //const requestInfo = { extract: { request: { id: 'request_id' } } }; + // sender (caution: only use metadata and public key when declare pair-up in oppoent entity) + const user = { email: 'user@esaml2.com'}; + const { id, context: SAMLResponse } = await idpcustomPrologNoEncrypt.createLoginResponse( + spNoAssertSignCustomConfig, + sampleRequestInfo, + 'post', + user, + createTemplateCallback(idpcustomPrologNoEncrypt, spNoAssertSignCustomConfig, binding.post, user), + ); + // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity) + const { samlContent, extract } = await spNoAssertSignCustomConfig.parseLoginResponse(idpcustomPrologNoEncrypt, 'post', { body: { SAMLResponse } }); + t.is(typeof id, 'string'); + t.is(samlContent.startsWith(xmlProlog), true); + t.is(samlContent.endsWith('/samlp:Response>'), true); + t.is(extract.nameID, 'user@esaml2.com'); + t.is(extract.attributes.name, 'mynameinsp'); + t.is(extract.attributes.mail, 'myemailassociatedwithsp@sp.com'); +}); + // + REDIRECT test('send response with signed assertion by redirect and parse it', async t => { // sender (caution: only use metadata and public key when declare pair-up in oppoent entity)