diff --git a/docs/signed-saml-response.md b/docs/signed-saml-response.md index acd3f9ee..4c1d70f3 100644 --- a/docs/signed-saml-response.md +++ b/docs/signed-saml-response.md @@ -39,6 +39,16 @@ There are different examples of signing scheme supported in samlify. To guarantee the setting in between idp-sp pair is synchronized, determination of assertion signature depends on the sp setting. Set `WantAssertionsSigned` to true in corresponding sp's metadata or `wantAssertionsSigned` in constructor if metadata is not set. ```javascript +const sp = ServiceProvider({ + // ... + metadata: readFileSync('./sp-metadata.xml'), + // must have if assertion signature fails validation + // transformationAlgorithms: [ + // 'http://www.w3.org/2000/09/xmldsig#enveloped-signature', + // 'http://www.w3.org/2001/10/xml-exc-c14n#', + // ], +}); + const idp = IdentityProvider({ // ... metadata: readFileSync('./idp-metatadata.xml'), diff --git a/src/binding-post.ts b/src/binding-post.ts index 8d6b9049..9e574923 100644 --- a/src/binding-post.ts +++ b/src/binding-post.ts @@ -140,6 +140,7 @@ async function base64LoginResponse(requestInfo: any = {}, entity: any, user: any rawSamlResponse = libsaml.constructSAMLSignature({ ...config, rawSamlMessage: rawSamlResponse, + transformationAlgorithms: spSetting.transformationAlgorithms, referenceTagXPath: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']", signatureConfig: { prefix: 'ds', diff --git a/src/types.ts b/src/types.ts index f74f3af6..bad667de 100644 --- a/src/types.ts +++ b/src/types.ts @@ -82,6 +82,7 @@ export interface ServiceProviderSettings { logoutRequestTemplate?: SAMLDocumentTemplate; signingCert?: string | Buffer; encryptCert?: string | Buffer; + transformationAlgorithms?: string[]; } export interface IdentityProviderSettings { diff --git a/test/flow.ts b/test/flow.ts index 863745e3..e6d6e0f6 100644 --- a/test/flow.ts +++ b/test/flow.ts @@ -265,6 +265,34 @@ test('send response with signed assertion and parse it', async t => { t.is(extract.response.inResponseTo, 'request_id'); }); +test('send response with signed assertion + custom transformation algorithms and parse it', async t => { + // sender (caution: only use metadata and public key when declare pair-up in oppoent entity) + const signedAssertionSp = serviceProvider( + { + ...defaultSpConfig, + transformationAlgorithms: [ + 'http://www.w3.org/2000/09/xmldsig#enveloped-signature', + 'http://www.w3.org/2001/10/xml-exc-c14n#' + ] + } + ); + + const user = { email: 'user@esaml2.com' }; + const { id, context: SAMLResponse } = await idpNoEncrypt.createLoginResponse(signedAssertionSp, sampleRequestInfo, 'post', user, createTemplateCallback(idpNoEncrypt, sp, user)); + // receiver (caution: only use metadata and public key when declare pair-up in oppoent entity) + const { samlContent, extract } = await sp.parseLoginResponse(idpNoEncrypt, 'post', { body: { SAMLResponse } }); + t.is(typeof id, 'string'); + t.is(samlContent.startsWith(''), true); + t.is(extract.nameID, 'user@esaml2.com'); + t.is(extract.response.inResponseTo, 'request_id'); + + // Verify xmldsig#enveloped-signature is included in the response + if (samlContent.indexOf('http://www.w3.org/2000/09/xmldsig#enveloped-signature') === -1) { + t.fail(); + } +}); + test('send response with [custom template] signed assertion and parse it', async t => { // sender (caution: only use metadata and public key when declare pair-up in oppoent entity) const requestInfo = { extract: { request: { id: 'request_id' } } };