samlesa 2.12.3 → 2.12.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of samlesa might be problematic. Click here for more details.

Files changed (60) hide show
  1. package/build/.idea/workspace.xml +13 -1
  2. package/build/index.js +54 -64
  3. package/build/index.js.map +1 -1
  4. package/build/src/api.js +24 -23
  5. package/build/src/api.js.map +1 -1
  6. package/build/src/binding-post.js +358 -368
  7. package/build/src/binding-post.js.map +1 -1
  8. package/build/src/binding-redirect.js +333 -332
  9. package/build/src/binding-redirect.js.map +1 -1
  10. package/build/src/binding-simplesign.js +222 -232
  11. package/build/src/binding-simplesign.js.map +1 -1
  12. package/build/src/entity-idp.js +130 -130
  13. package/build/src/entity-idp.js.map +1 -1
  14. package/build/src/entity-sp.js +96 -96
  15. package/build/src/entity-sp.js.map +1 -1
  16. package/build/src/entity.js +225 -235
  17. package/build/src/entity.js.map +1 -1
  18. package/build/src/extractor.js +385 -369
  19. package/build/src/extractor.js.map +1 -1
  20. package/build/src/flow.js +320 -319
  21. package/build/src/flow.js.map +1 -1
  22. package/build/src/libsaml.js +665 -641
  23. package/build/src/libsaml.js.map +1 -1
  24. package/build/src/metadata-idp.js +127 -127
  25. package/build/src/metadata-idp.js.map +1 -1
  26. package/build/src/metadata-sp.js +231 -231
  27. package/build/src/metadata-sp.js.map +1 -1
  28. package/build/src/metadata.js +166 -176
  29. package/build/src/metadata.js.map +1 -1
  30. package/build/src/types.js +11 -11
  31. package/build/src/urn.js +212 -212
  32. package/build/src/urn.js.map +1 -1
  33. package/build/src/utility.js +292 -248
  34. package/build/src/utility.js.map +1 -1
  35. package/build/src/validator.js +27 -26
  36. package/build/src/validator.js.map +1 -1
  37. package/package.json +8 -10
  38. package/src/api.ts +1 -1
  39. package/src/binding-redirect.ts +83 -64
  40. package/src/extractor.ts +23 -5
  41. package/src/libsaml.ts +95 -62
  42. package/src/utility.ts +147 -76
  43. package/types/index.d.ts +10 -10
  44. package/types/src/api.d.ts +13 -13
  45. package/types/src/binding-post.d.ts +46 -46
  46. package/types/src/binding-redirect.d.ts +52 -52
  47. package/types/src/binding-simplesign.d.ts +39 -39
  48. package/types/src/entity-idp.d.ts +42 -42
  49. package/types/src/entity-sp.d.ts +36 -36
  50. package/types/src/entity.d.ts +101 -99
  51. package/types/src/extractor.d.ts +25 -25
  52. package/types/src/flow.d.ts +6 -6
  53. package/types/src/libsaml.d.ts +200 -210
  54. package/types/src/metadata-idp.d.ts +24 -24
  55. package/types/src/metadata-sp.d.ts +36 -36
  56. package/types/src/metadata.d.ts +59 -57
  57. package/types/src/types.d.ts +129 -127
  58. package/types/src/urn.d.ts +194 -194
  59. package/types/src/utility.d.ts +134 -134
  60. package/types/src/validator.d.ts +3 -3
@@ -1,642 +1,666 @@
1
- "use strict";
2
- /**
3
- * @file SamlLib.js
4
- * @author tngan
5
- * @desc A simple library including some common functions
6
- */
7
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
8
- if (k2 === undefined) k2 = k;
9
- var desc = Object.getOwnPropertyDescriptor(m, k);
10
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
11
- desc = { enumerable: true, get: function() { return m[k]; } };
12
- }
13
- Object.defineProperty(o, k2, desc);
14
- }) : (function(o, m, k, k2) {
15
- if (k2 === undefined) k2 = k;
16
- o[k2] = m[k];
17
- }));
18
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
19
- Object.defineProperty(o, "default", { enumerable: true, value: v });
20
- }) : function(o, v) {
21
- o["default"] = v;
22
- });
23
- var __importStar = (this && this.__importStar) || (function () {
24
- var ownKeys = function(o) {
25
- ownKeys = Object.getOwnPropertyNames || function (o) {
26
- var ar = [];
27
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
28
- return ar;
29
- };
30
- return ownKeys(o);
31
- };
32
- return function (mod) {
33
- if (mod && mod.__esModule) return mod;
34
- var result = {};
35
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
36
- __setModuleDefault(result, mod);
37
- return result;
38
- };
39
- })();
40
- var __importDefault = (this && this.__importDefault) || function (mod) {
41
- return (mod && mod.__esModule) ? mod : { "default": mod };
42
- };
43
- Object.defineProperty(exports, "__esModule", { value: true });
44
- const utility_js_1 = __importStar(require("./utility.js"));
45
- const urn_js_1 = require("./urn.js");
46
- const xpath_1 = require("xpath");
47
- const node_rsa_1 = __importDefault(require("node-rsa"));
48
- const xml_crypto_1 = require("xml-crypto");
49
- const xmlenc = __importStar(require("xml-encryption"));
50
- const camelcase_1 = __importDefault(require("camelcase"));
51
- const api_js_1 = require("./api.js");
52
- const xml_escape_1 = __importDefault(require("xml-escape"));
53
- const fs = __importStar(require("fs"));
54
- const xmldom_1 = require("@xmldom/xmldom");
55
- const signatureAlgorithms = urn_js_1.algorithms.signature;
56
- const digestAlgorithms = urn_js_1.algorithms.digest;
57
- const certUse = urn_js_1.wording.certUse;
58
- const urlParams = urn_js_1.wording.urlParams;
59
- const libSaml = () => {
60
- /**
61
- * @desc helper function to get back the query param for redirect binding for SLO/SSO
62
- * @type {string}
63
- */
64
- function getQueryParamByType(type) {
65
- if ([urlParams.logoutRequest, urlParams.samlRequest].indexOf(type) !== -1) {
66
- return 'SAMLRequest';
67
- }
68
- if ([urlParams.logoutResponse, urlParams.samlResponse].indexOf(type) !== -1) {
69
- return 'SAMLResponse';
70
- }
71
- throw new Error('ERR_UNDEFINED_QUERY_PARAMS');
72
- }
73
- /**
74
- *
75
- */
76
- const nrsaAliasMapping = {
77
- 'http://www.w3.org/2000/09/xmldsig#rsa-sha1': 'pkcs1-sha1',
78
- 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256': 'pkcs1-sha256',
79
- 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512': 'pkcs1-sha512',
80
- };
81
- /**
82
- * @desc Default login request template
83
- * @type {LoginRequestTemplate}
84
- */
85
- const defaultLoginRequestTemplate = {
86
- context: '<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="{AssertionConsumerServiceURL}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:NameIDPolicy Format="{NameIDFormat}" AllowCreate="{AllowCreate}"/></samlp:AuthnRequest>',
87
- };
88
- /**
89
- * @desc Default logout request template
90
- * @type {LogoutRequestTemplate}
91
- */
92
- const defaultLogoutRequestTemplate = {
93
- context: '<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}"><saml:Issuer>{Issuer}</saml:Issuer><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID></samlp:LogoutRequest>',
94
- };
95
- /**
96
- * @desc Default AttributeStatement template
97
- * @type {AttributeStatementTemplate}
98
- */
99
- const defaultAttributeStatementTemplate = {
100
- context: '<saml:AttributeStatement>{Attributes}</saml:AttributeStatement>',
101
- };
102
- /**
103
- * @desc Default Attribute template
104
- * @type {AttributeTemplate}
105
- */
106
- const defaultAttributeTemplate = {
107
- context: '<saml:Attribute Name="{Name}" NameFormat="{NameFormat}"><saml:AttributeValue xmlns:xs="{ValueXmlnsXs}" xmlns:xsi="{ValueXmlnsXsi}" xsi:type="{ValueXsiType}">{Value}</saml:AttributeValue></saml:Attribute>',
108
- };
109
- /**
110
- * @desc Default login response template
111
- * @type {LoginResponseTemplate}
112
- */
113
- const defaultLoginResponseTemplate = {
114
- context: '<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" InResponseTo="{InResponseTo}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="{StatusCode}"/></samlp:Status><saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{AssertionID}" Version="2.0" IssueInstant="{IssueInstant}"><saml:Issuer>{Issuer}</saml:Issuer><saml:Subject><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="{SubjectConfirmationDataNotOnOrAfter}" Recipient="{SubjectRecipient}" InResponseTo="{InResponseTo}"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="{ConditionsNotBefore}" NotOnOrAfter="{ConditionsNotOnOrAfter}"><saml:AudienceRestriction><saml:Audience>{Audience}</saml:Audience></saml:AudienceRestriction></saml:Conditions>{AuthnStatement}{AttributeStatement}</saml:Assertion></samlp:Response>',
115
- attributes: [],
116
- additionalTemplates: {
117
- 'attributeStatementTemplate': defaultAttributeStatementTemplate,
118
- 'attributeTemplate': defaultAttributeTemplate
119
- }
120
- };
121
- /**
122
- * @desc Default logout response template
123
- * @type {LogoutResponseTemplate}
124
- */
125
- const defaultLogoutResponseTemplate = {
126
- context: '<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" InResponseTo="{InResponseTo}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="{StatusCode}"/></samlp:Status></samlp:LogoutResponse>',
127
- };
128
- /**
129
- * @private
130
- * @desc Get the signing scheme alias by signature algorithms, used by the node-rsa module
131
- * @param {string} sigAlg signature algorithm
132
- * @return {string/null} signing algorithm short-hand for the module node-rsa
133
- */
134
- function getSigningScheme(sigAlg) {
135
- if (sigAlg) {
136
- const algAlias = nrsaAliasMapping[sigAlg];
137
- if (!(algAlias === undefined)) {
138
- return algAlias;
139
- }
140
- }
141
- return nrsaAliasMapping[signatureAlgorithms.RSA_SHA1];
142
- }
143
- /**
144
- * @private
145
- * @desc Get the digest algorithms by signature algorithms
146
- * @param {string} sigAlg signature algorithm
147
- * @return {string/undefined} digest algorithm
148
- */
149
- function getDigestMethod(sigAlg) {
150
- return digestAlgorithms[sigAlg];
151
- }
152
- /**
153
- * @public
154
- * @desc Create XPath
155
- * @param {string/object} local parameters to create XPath
156
- * @param {boolean} isExtractAll define whether returns whole content according to the XPath
157
- * @return {string} xpath
158
- */
159
- function createXPath(local, isExtractAll) {
160
- if ((0, utility_js_1.isString)(local)) {
161
- return isExtractAll === true ? "//*[local-name(.)='" + local + "']/text()" : "//*[local-name(.)='" + local + "']";
162
- }
163
- return "//*[local-name(.)='" + local.name + "']/@" + local.attr;
164
- }
165
- /**
166
- * @private
167
- * @desc Tag normalization
168
- * @param {string} prefix prefix of the tag
169
- * @param {content} content normalize it to capitalized camel case
170
- * @return {string}
171
- */
172
- function tagging(prefix, content) {
173
- const camelContent = (0, camelcase_1.default)(content, { locale: 'en-us' });
174
- return prefix + camelContent.charAt(0).toUpperCase() + camelContent.slice(1);
175
- }
176
- function escapeTag(replacement) {
177
- return (_match, quote) => {
178
- const text = (replacement === null || replacement === undefined) ? '' : String(replacement);
179
- // not having a quote means this interpolation isn't for an attribute, and so does not need escaping
180
- return quote ? `${quote}${(0, xml_escape_1.default)(text)}` : text;
181
- };
182
- }
183
- return {
184
- createXPath,
185
- getQueryParamByType,
186
- defaultLoginRequestTemplate,
187
- defaultLoginResponseTemplate,
188
- defaultAttributeStatementTemplate,
189
- defaultAttributeTemplate,
190
- defaultLogoutRequestTemplate,
191
- defaultLogoutResponseTemplate,
192
- /**
193
- * @desc Replace the tag (e.g. {tag}) inside the raw XML
194
- * @param {string} rawXML raw XML string used to do keyword replacement
195
- * @param {array} tagValues tag values
196
- * @return {string}
197
- */
198
- replaceTagsByValue(rawXML, tagValues) {
199
- Object.keys(tagValues).forEach(t => {
200
- rawXML = rawXML.replace(new RegExp(`("?)\\{${t}\\}`, 'g'), escapeTag(tagValues[t]));
201
- });
202
- return rawXML;
203
- },
204
- /**
205
- * @desc Helper function to build the AttributeStatement tag
206
- * @param {LoginResponseAttribute} attributes an array of attribute configuration
207
- * @param {AttributeTemplate} attributeTemplate the attribute tag template to be used
208
- * @param {AttributeStatementTemplate} attributeStatementTemplate the attributeStatement tag template to be used
209
- * @return {string}
210
- */
211
- attributeStatementBuilder(attributes, attributeTemplate = defaultAttributeTemplate, attributeStatementTemplate = defaultAttributeStatementTemplate) {
212
- const attr = attributes.map(({ name, nameFormat, valueTag, valueXsiType, type, valueXmlnsXs, valueXmlnsXsi }) => {
213
- const defaultValueXmlnsXs = 'http://www.w3.org/2001/XMLSchema';
214
- const defaultValueXmlnsXsi = 'http://www.w3.org/2001/XMLSchema-instance';
215
- let attributeLine = attributeTemplate.context;
216
- if (attributeLine && typeof attributeLine === 'function') {
217
- // 安全调用
218
- // @ts-ignore
219
- return attributeLine({ name, nameFormat, valueTag, valueXsiType, type, valueXmlnsXs: valueXmlnsXs ?? defaultValueXmlnsXs, valueXmlnsXsi: valueXmlnsXsi ?? defaultValueXmlnsXsi });
220
- }
221
- else {
222
- attributeLine = attributeLine.replace('{Name}', name);
223
- attributeLine = attributeLine.replace('{NameFormat}', nameFormat);
224
- attributeLine = attributeLine.replace('{ValueXmlnsXs}', valueXmlnsXs ? valueXmlnsXs : defaultValueXmlnsXs);
225
- attributeLine = attributeLine.replace('{ValueXmlnsXsi}', valueXmlnsXsi ? valueXmlnsXsi : defaultValueXmlnsXsi);
226
- attributeLine = attributeLine.replace('{ValueXsiType}', valueXsiType);
227
- attributeLine = attributeLine.replace('{Value}', `{${tagging('attr', valueTag)}}`);
228
- return attributeLine;
229
- }
230
- }).join('');
231
- return attributeStatementTemplate.context.replace('{Attributes}', attr);
232
- },
233
- /**
234
- * @desc Construct the XML signature for POST binding
235
- * @param {string} rawSamlMessage request/response xml string
236
- * @param {string} referenceTagXPath reference uri
237
- * @param {string} privateKey declares the private key
238
- * @param {string} passphrase passphrase of the private key [optional]
239
- * @param {string|buffer} signingCert signing certificate
240
- * @param {string} signatureAlgorithm signature algorithm
241
- * @param {string[]} transformationAlgorithms canonicalization and transformation Algorithms
242
- * @return {string} base64 encoded string
243
- */
244
- constructSAMLSignature(opts) {
245
- const { rawSamlMessage, referenceTagXPath, privateKey, privateKeyPass, signatureAlgorithm = signatureAlgorithms.RSA_SHA512, transformationAlgorithms = [
246
- 'http://www.w3.org/2000/09/xmldsig#enveloped-signature',
247
- 'http://www.w3.org/2001/10/xml-exc-c14n#',
248
- ], signingCert, signatureConfig, isBase64Output = true, isMessageSigned = false, } = opts;
249
- const sig = new xml_crypto_1.SignedXml();
250
- // Add assertion sections as reference
251
- const digestAlgorithm = getDigestMethod(signatureAlgorithm);
252
- if (referenceTagXPath) {
253
- sig.addReference({
254
- xpath: referenceTagXPath,
255
- transforms: transformationAlgorithms,
256
- digestAlgorithm: digestAlgorithm
257
- });
258
- }
259
- if (isMessageSigned) {
260
- sig.addReference({
261
- // reference to the root node
262
- xpath: '/*',
263
- transforms: transformationAlgorithms,
264
- digestAlgorithm
265
- });
266
- }
267
- sig.signatureAlgorithm = signatureAlgorithm;
268
- sig.publicCert = this.getKeyInfo(signingCert, signatureConfig).getKey();
269
- sig.getKeyInfoContent = this.getKeyInfo(signingCert, signatureConfig).getKeyInfo;
270
- sig.privateKey = utility_js_1.default.readPrivateKey(privateKey, privateKeyPass, true);
271
- sig.canonicalizationAlgorithm = 'http://www.w3.org/2001/10/xml-exc-c14n#';
272
- if (signatureConfig) {
273
- sig.computeSignature(rawSamlMessage, signatureConfig);
274
- }
275
- else {
276
- sig.computeSignature(rawSamlMessage);
277
- }
278
- return isBase64Output !== false ? utility_js_1.default.base64Encode(sig.getSignedXml()) : sig.getSignedXml();
279
- },
280
- /**
281
- * @desc Verify the XML signature
282
- * @param {string} xml xml
283
- * @param {SignatureVerifierOptions} opts cert declares the X509 certificate
284
- * @return {[boolean, string | null]} - A tuple where:
285
- * - The first element is `true` if the signature is valid, `false` otherwise.
286
- * - The second element is the cryptographically authenticated assertion node as a string, or `null` if not found.
287
- */
288
- verifySignature(xml, opts) {
289
- const { dom } = (0, api_js_1.getContext)();
290
- const doc = dom.parseFromString(xml);
291
- const docParser = new xmldom_1.DOMParser();
292
- // In order to avoid the wrapping attack, we have changed to use absolute xpath instead of naively fetching the signature element
293
- // message signature (logout response / saml response)
294
- const messageSignatureXpath = "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Signature']";
295
- // assertion signature (logout response / saml response)
296
- const assertionSignatureXpath = "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']/*[local-name(.)='Signature']";
297
- // check if there is a potential malicious wrapping signature
298
- const wrappingElementsXPath = "/*[contains(local-name(), 'Response')]/*[local-name(.)='Assertion']/*[local-name(.)='Subject']/*[local-name(.)='SubjectConfirmation']/*[local-name(.)='SubjectConfirmationData']//*[local-name(.)='Assertion' or local-name(.)='Signature']";
299
- // select the signature node
300
- let selection = [];
301
- const messageSignatureNode = (0, xpath_1.select)(messageSignatureXpath, doc);
302
- const assertionSignatureNode = (0, xpath_1.select)(assertionSignatureXpath, doc);
303
- const wrappingElementNode = (0, xpath_1.select)(wrappingElementsXPath, doc);
304
- selection = selection.concat(messageSignatureNode);
305
- selection = selection.concat(assertionSignatureNode);
306
- // try to catch potential wrapping attack
307
- if (wrappingElementNode.length !== 0) {
308
- throw new Error('ERR_POTENTIAL_WRAPPING_ATTACK');
309
- }
310
- // guarantee to have a signature in saml response
311
- if (selection.length === 0) {
312
- throw new Error('ERR_ZERO_SIGNATURE');
313
- }
314
- // need to refactor later on
315
- for (const signatureNode of selection) {
316
- const sig = new xml_crypto_1.SignedXml();
317
- let verified = false;
318
- sig.signatureAlgorithm = opts.signatureAlgorithm;
319
- if (!opts.keyFile && !opts.metadata) {
320
- throw new Error('ERR_UNDEFINED_SIGNATURE_VERIFIER_OPTIONS');
321
- }
322
- if (opts.keyFile) {
323
- sig.publicCert = fs.readFileSync(opts.keyFile);
324
- }
325
- if (opts.metadata) {
326
- const certificateNode = (0, xpath_1.select)(".//*[local-name(.)='X509Certificate']", signatureNode);
327
- // certificate in metadata
328
- let metadataCert = opts.metadata.getX509Certificate(certUse.signing);
329
- // flattens the nested array of Certificates from each KeyDescriptor
330
- if (Array.isArray(metadataCert)) {
331
- metadataCert = (0, utility_js_1.flattenDeep)(metadataCert);
332
- }
333
- else if (typeof metadataCert === 'string') {
334
- metadataCert = [metadataCert];
335
- }
336
- // normalise the certificate string
337
- metadataCert = metadataCert.map(utility_js_1.default.normalizeCerString);
338
- // no certificate in node response nor metadata
339
- if (certificateNode.length === 0 && metadataCert.length === 0) {
340
- throw new Error('NO_SELECTED_CERTIFICATE');
341
- }
342
- // certificate node in response
343
- if (certificateNode.length !== 0) {
344
- const x509CertificateData = certificateNode[0].firstChild.data;
345
- const x509Certificate = utility_js_1.default.normalizeCerString(x509CertificateData);
346
- if (metadataCert.length >= 1 &&
347
- !metadataCert.find(cert => cert.trim() === x509Certificate.trim())) {
348
- // keep this restriction for rolling certificate usage
349
- // to make sure the response certificate is one of those specified in metadata
350
- throw new Error('ERROR_UNMATCH_CERTIFICATE_DECLARATION_IN_METADATA');
351
- }
352
- sig.publicCert = this.getKeyInfo(x509Certificate).getKey();
353
- }
354
- else {
355
- // Select first one from metadata
356
- sig.publicCert = this.getKeyInfo(metadataCert[0]).getKey();
357
- }
358
- }
359
- sig.loadSignature(signatureNode);
360
- doc.removeChild(signatureNode);
361
- verified = sig.checkSignature(doc.toString());
362
- // immediately throw error when any one of the signature is failed to get verified
363
- if (!verified) {
364
- throw new Error('ERR_FAILED_TO_VERIFY_SIGNATURE');
365
- }
366
- // attempt is made to get the signed Reference as a string();
367
- // note, we don't have access to the actual signedReferences API unfortunately
368
- // mainly a sanity check here for SAML. (Although ours would still be secure, if multiple references are used)
369
- if (!(sig.getReferences().length >= 1)) {
370
- throw new Error('NO_SIGNATURE_REFERENCES');
371
- }
372
- const signedVerifiedXML = sig.getSignedReferences()[0];
373
- const rootNode = docParser.parseFromString(signedVerifiedXML, 'text/xml').documentElement;
374
- // process the verified signature:
375
- // case 1, rootSignedDoc is a response:
376
- if (rootNode.localName === 'Response') {
377
- // try getting the Xml from the first assertion
378
- const EncryptedAssertions = (0, xpath_1.select)("./*[local-name()='EncryptedAssertion']", rootNode);
379
- const assertions = (0, xpath_1.select)("./*[local-name()='Assertion']", rootNode);
380
- // now we can process the assertion as an assertion
381
- if (EncryptedAssertions.length === 1) {
382
- return [true, EncryptedAssertions[0].toString()];
383
- }
384
- if (assertions.length === 1) {
385
- return [true, assertions[0].toString()];
386
- }
387
- }
388
- else if (rootNode.localName === 'Assertion') {
389
- return [true, rootNode.toString()];
390
- }
391
- else {
392
- return [true, null]; // signature is valid. But there is no assertion node here. It could be metadata node, hence return null
393
- }
394
- }
395
- ;
396
- // something has gone seriously wrong if we are still here
397
- throw new Error('ERR_ZERO_SIGNATURE');
398
- // response must be signed, either entire document or assertion
399
- // default we will take the assertion section under root
400
- /* if (messageSignatureNode.length === 1) {
401
- const node = select("/!*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/!*[local-name(.)='Assertion']", doc);
402
- if (node.length === 1) {
403
- assertionNode = node[0].toString();
404
- }
405
- }
406
-
407
- if (assertionSignatureNode.length === 1) {
408
- const verifiedAssertionInfo = extract(assertionSignatureNode[0].toString(), [{
409
- key: 'refURI',
410
- localPath: ['Signature', 'SignedInfo', 'Reference'],
411
- attributes: ['URI']
412
- }]);
413
- // get the assertion supposed to be the one should be verified
414
- const desiredAssertionInfo = extract(doc.toString(), [{
415
- key: 'id',
416
- localPath: ['~Response', 'Assertion'],
417
- attributes: ['ID']
418
- }]);
419
- // 5.4.2 References
420
- // SAML assertions and protocol messages MUST supply a value for the ID attribute on the root element of
421
- // the assertion or protocol message being signed. The assertion’s or protocol message's root element may
422
- // or may not be the root element of the actual XML document containing the signed assertion or protocol
423
- // message (e.g., it might be contained within a SOAP envelope).
424
- // Signatures MUST contain a single <ds:Reference> containing a same-document reference to the ID
425
- // attribute value of the root element of the assertion or protocol message being signed. For example, if the
426
- // ID attribute value is "foo", then the URI attribute in the <ds:Reference> element MUST be "#foo".
427
- if (verifiedAssertionInfo.refURI !== `#${desiredAssertionInfo.id}`) {
428
- throw new Error('ERR_POTENTIAL_WRAPPING_ATTACK');
429
- }
430
- const verifiedDoc = extract(doc.toString(), [{
431
- key: 'assertion',
432
- localPath: ['~Response', 'Assertion'],
433
- attributes: [],
434
- context: true
435
- }]);
436
- assertionNode = verifiedDoc.assertion.toString();
437
- }
438
-
439
- return [verified, assertionNode];*/
440
- },
441
- /**
442
- * @desc Helper function to create the key section in metadata (abstraction for signing and encrypt use)
443
- * @param {string} use type of certificate (e.g. signing, encrypt)
444
- * @param {string} certString declares the certificate String
445
- * @return {object} object used in xml module
446
- */
447
- createKeySection(use, certString) {
448
- return {
449
- ['KeyDescriptor']: [
450
- {
451
- _attr: { use },
452
- },
453
- {
454
- ['ds:KeyInfo']: [
455
- {
456
- _attr: {
457
- 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
458
- },
459
- },
460
- {
461
- ['ds:X509Data']: [{
462
- 'ds:X509Certificate': utility_js_1.default.normalizeCerString(certString),
463
- }],
464
- },
465
- ],
466
- }
467
- ],
468
- };
469
- },
470
- /**
471
- * @desc Constructs SAML message
472
- * @param {string} octetString see "Bindings for the OASIS Security Assertion Markup Language (SAML V2.0)" P.17/46
473
- * @param {string} key declares the pem-formatted private key
474
- * @param {string} passphrase passphrase of private key [optional]
475
- * @param {string} signingAlgorithm signing algorithm
476
- * @return {string} message signature
477
- */
478
- constructMessageSignature(octetString, key, passphrase, isBase64, signingAlgorithm) {
479
- // Default returning base64 encoded signature
480
- // Embed with node-rsa module
481
- const decryptedKey = new node_rsa_1.default(utility_js_1.default.readPrivateKey(key, passphrase), undefined, {
482
- signingScheme: getSigningScheme(signingAlgorithm),
483
- });
484
- const signature = decryptedKey.sign(octetString);
485
- // Use private key to sign data
486
- return isBase64 !== false ? signature.toString('base64') : signature;
487
- },
488
- /**
489
- * @desc Verifies message signature
490
- * @param {Metadata} metadata metadata object of identity provider or service provider
491
- * @param {string} octetString see "Bindings for the OASIS Security Assertion Markup Language (SAML V2.0)" P.17/46
492
- * @param {string} signature context of XML signature
493
- * @param {string} verifyAlgorithm algorithm used to verify
494
- * @return {boolean} verification result
495
- */
496
- verifyMessageSignature(metadata, octetString, signature, verifyAlgorithm) {
497
- const signCert = metadata.getX509Certificate(certUse.signing);
498
- const signingScheme = getSigningScheme(verifyAlgorithm);
499
- const key = new node_rsa_1.default(utility_js_1.default.getPublicKeyPemFromCertificate(signCert), 'public', { signingScheme });
500
- return key.verify(Buffer.from(octetString), Buffer.from(signature));
501
- },
502
- /**
503
- * @desc Get the public key in string format
504
- * @param {string} x509Certificate certificate
505
- * @return {string} public key
506
- */
507
- getKeyInfo(x509Certificate, signatureConfig = {}) {
508
- const prefix = signatureConfig.prefix ? `${signatureConfig.prefix}:` : '';
509
- return {
510
- getKeyInfo: () => {
511
- return `<${prefix}X509Data><${prefix}X509Certificate>${x509Certificate}</${prefix}X509Certificate></${prefix}X509Data>`;
512
- },
513
- getKey: () => {
514
- return utility_js_1.default.getPublicKeyPemFromCertificate(x509Certificate).toString();
515
- },
516
- };
517
- },
518
- /**
519
- * @desc Encrypt the assertion section in Response
520
- * @param {Entity} sourceEntity source entity
521
- * @param {Entity} targetEntity target entity
522
- * @param {string} xml response in xml string format
523
- * @return {Promise} a promise to resolve the finalized xml
524
- */
525
- encryptAssertion(sourceEntity, targetEntity, xml) {
526
- // Implement encryption after signature if it has
527
- return new Promise((resolve, reject) => {
528
- if (!xml) {
529
- return reject(new Error('ERR_UNDEFINED_ASSERTION'));
530
- }
531
- const sourceEntitySetting = sourceEntity.entitySetting;
532
- const targetEntityMetadata = targetEntity.entityMeta;
533
- const { dom } = (0, api_js_1.getContext)();
534
- const doc = dom.parseFromString(xml);
535
- const assertions = (0, xpath_1.select)("//*[local-name(.)='Assertion']", doc);
536
- if (!Array.isArray(assertions) || assertions.length === 0) {
537
- throw new Error('ERR_NO_ASSERTION');
538
- }
539
- if (assertions.length > 1) {
540
- throw new Error('ERR_MULTIPLE_ASSERTION');
541
- }
542
- const rawAssertionNode = assertions[0];
543
- // Perform encryption depends on the setting, default is false
544
- if (sourceEntitySetting.isAssertionEncrypted) {
545
- const publicKeyPem = utility_js_1.default.getPublicKeyPemFromCertificate(targetEntityMetadata.getX509Certificate(certUse.encrypt));
546
- xmlenc.encrypt(rawAssertionNode.toString(), {
547
- // use xml-encryption module
548
- rsa_pub: Buffer.from(publicKeyPem), // public key from certificate
549
- pem: Buffer.from(`-----BEGIN CERTIFICATE-----${targetEntityMetadata.getX509Certificate(certUse.encrypt)}-----END CERTIFICATE-----`),
550
- encryptionAlgorithm: sourceEntitySetting.dataEncryptionAlgorithm,
551
- keyEncryptionAlgorithm: sourceEntitySetting.keyEncryptionAlgorithm,
552
- keyEncryptionDigest: 'SHA-512',
553
- disallowEncryptionWithInsecureAlgorithm: true,
554
- warnInsecureAlgorithm: true
555
- }, (err, res) => {
556
- if (err) {
557
- console.error(err);
558
- return reject(new Error('ERR_EXCEPTION_OF_ASSERTION_ENCRYPTION'));
559
- }
560
- if (!res) {
561
- return reject(new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION'));
562
- }
563
- const { encryptedAssertion: encAssertionPrefix } = sourceEntitySetting.tagPrefix;
564
- const encryptAssertionDoc = dom.parseFromString(`<${encAssertionPrefix}:EncryptedAssertion xmlns:${encAssertionPrefix}="${urn_js_1.namespace.names.assertion}">${res}</${encAssertionPrefix}:EncryptedAssertion>`);
565
- doc.documentElement.replaceChild(encryptAssertionDoc.documentElement, rawAssertionNode);
566
- return resolve(utility_js_1.default.base64Encode(doc.toString()));
567
- });
568
- }
569
- else {
570
- return resolve(utility_js_1.default.base64Encode(xml)); // No need to do encryption
571
- }
572
- });
573
- },
574
- /**
575
- * @desc Decrypt the assertion section in Response
576
- * @param {string} type only accept SAMLResponse to proceed decryption
577
- * @param {Entity} here this entity
578
- * @param {Entity} from from the entity where the message is sent
579
- * @param {string} entireXML response in xml string format
580
- * @return {function} a promise to get back the entire xml with decrypted assertion
581
- */
582
- decryptAssertion(here, entireXML) {
583
- return new Promise((resolve, reject) => {
584
- // Implement decryption first then check the signature
585
- if (!entireXML) {
586
- return reject(new Error('ERR_UNDEFINED_ASSERTION'));
587
- }
588
- // Perform encryption depends on the setting of where the message is sent, default is false
589
- const hereSetting = here.entitySetting;
590
- const { dom } = (0, api_js_1.getContext)();
591
- const doc = dom.parseFromString(entireXML);
592
- const encryptedAssertions = (0, xpath_1.select)("/*[contains(local-name(), 'Response')]/*[local-name(.)='EncryptedAssertion']", doc);
593
- if (!Array.isArray(encryptedAssertions) || encryptedAssertions.length === 0) {
594
- throw new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION');
595
- }
596
- if (encryptedAssertions.length > 1) {
597
- throw new Error('ERR_MULTIPLE_ASSERTION');
598
- }
599
- const encAssertionNode = encryptedAssertions[0];
600
- return xmlenc.decrypt(encAssertionNode.toString(), {
601
- key: utility_js_1.default.readPrivateKey(hereSetting.encPrivateKey, hereSetting.encPrivateKeyPass),
602
- }, (err, res) => {
603
- if (err) {
604
- console.error(err);
605
- return reject(new Error('ERR_EXCEPTION_OF_ASSERTION_DECRYPTION'));
606
- }
607
- if (!res) {
608
- return reject(new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION'));
609
- }
610
- const rawAssertionDoc = dom.parseFromString(res);
611
- doc.documentElement.replaceChild(rawAssertionDoc.documentElement, encAssertionNode);
612
- return resolve([doc.toString(), res]);
613
- });
614
- });
615
- },
616
- /**
617
- * @desc Check if the xml string is valid and bounded
618
- */
619
- async isValidXml(input) {
620
- // check if global api contains the validate function
621
- const { validate } = (0, api_js_1.getContext)();
622
- /**
623
- * user can write a validate function that always returns
624
- * a resolved promise and skip the validator even in
625
- * production, user will take the responsibility if
626
- * they intend to skip the validation
627
- */
628
- if (!validate) {
629
- // otherwise, an error will be thrown
630
- return Promise.reject('Your application is potentially vulnerable because no validation function found. Please read the documentation on how to setup the validator. (https://github.com/tngan/samlify#installation)');
631
- }
632
- try {
633
- return await validate(input);
634
- }
635
- catch (e) {
636
- throw e;
637
- }
638
- },
639
- };
640
- };
641
- exports.default = libSaml();
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
25
+ var __importDefault = (this && this.__importDefault) || function (mod) {
26
+ return (mod && mod.__esModule) ? mod : { "default": mod };
27
+ };
28
+ Object.defineProperty(exports, "__esModule", { value: true });
29
+ /**
30
+ * @file SamlLib.js
31
+ * @author tngan
32
+ * @desc A simple library including some common functions
33
+ */
34
+ const node_crypto_1 = require("node:crypto");
35
+ const utility_js_1 = __importStar(require("./utility.js"));
36
+ const urn_js_1 = require("./urn.js");
37
+ const xpath_1 = require("xpath");
38
+ const xml_crypto_1 = require("xml-crypto");
39
+ const xmlenc = __importStar(require("xml-encryption"));
40
+ const camelcase_1 = __importDefault(require("camelcase"));
41
+ const api_js_1 = require("./api.js");
42
+ const xml_escape_1 = __importDefault(require("xml-escape"));
43
+ const fs = __importStar(require("fs"));
44
+ const xmldom_1 = require("@xmldom/xmldom");
45
+ const signatureAlgorithms = urn_js_1.algorithms.signature;
46
+ const digestAlgorithms = urn_js_1.algorithms.digest;
47
+ const certUse = urn_js_1.wording.certUse;
48
+ const urlParams = urn_js_1.wording.urlParams;
49
+ /**
50
+ * 算法名称映射表 (兼容 X.509 和 SAML 规范)
51
+ */
52
+ function mapSignAlgorithm(algorithm) {
53
+ const algorithmMap = {
54
+ 'rsa-sha1': 'RSA-SHA1',
55
+ 'rsa-sha256': 'RSA-SHA256',
56
+ 'rsa-sha384': 'RSA-SHA384',
57
+ 'rsa-sha512': 'RSA-SHA512',
58
+ 'ecdsa-sha256': 'ECDSA-SHA256',
59
+ 'ecdsa-sha384': 'ECDSA-SHA384',
60
+ 'ecdsa-sha512': 'ECDSA-SHA512'
61
+ };
62
+ return algorithmMap[algorithm.toLowerCase()] || algorithm;
63
+ }
64
+ const libSaml = () => {
65
+ /**
66
+ * @desc helper function to get back the query param for redirect binding for SLO/SSO
67
+ * @type {string}
68
+ */
69
+ function getQueryParamByType(type) {
70
+ if ([urlParams.logoutRequest, urlParams.samlRequest].indexOf(type) !== -1) {
71
+ return 'SAMLRequest';
72
+ }
73
+ if ([urlParams.logoutResponse, urlParams.samlResponse].indexOf(type) !== -1) {
74
+ return 'SAMLResponse';
75
+ }
76
+ throw new Error('ERR_UNDEFINED_QUERY_PARAMS');
77
+ }
78
+ /**
79
+ *
80
+ */
81
+ const nrsaAliasMapping = {
82
+ 'http://www.w3.org/2000/09/xmldsig#rsa-sha1': 'pkcs1-sha1',
83
+ 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256': 'pkcs1-sha256',
84
+ 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512': 'pkcs1-sha512',
85
+ };
86
+ const nrsaAliasMappingForNode = {
87
+ 'http://www.w3.org/2000/09/xmldsig#rsa-sha1': 'RSA-SHA1',
88
+ 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha256': 'RSA-SHA256',
89
+ 'http://www.w3.org/2001/04/xmldsig-more#rsa-sha512': 'RSA-SHA512',
90
+ };
91
+ /**
92
+ * @desc Default login request template
93
+ * @type {LoginRequestTemplate}
94
+ */
95
+ const defaultLoginRequestTemplate = {
96
+ context: '<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" ProtocolBinding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" AssertionConsumerServiceURL="{AssertionConsumerServiceURL}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:NameIDPolicy Format="{NameIDFormat}" AllowCreate="{AllowCreate}"/></samlp:AuthnRequest>',
97
+ };
98
+ /**
99
+ * @desc Default logout request template
100
+ * @type {LogoutRequestTemplate}
101
+ */
102
+ const defaultLogoutRequestTemplate = {
103
+ context: '<samlp:LogoutRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}"><saml:Issuer>{Issuer}</saml:Issuer><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID></samlp:LogoutRequest>',
104
+ };
105
+ /**
106
+ * @desc Default AttributeStatement template
107
+ * @type {AttributeStatementTemplate}
108
+ */
109
+ const defaultAttributeStatementTemplate = {
110
+ context: '<saml:AttributeStatement>{Attributes}</saml:AttributeStatement>',
111
+ };
112
+ /**
113
+ * @desc Default Attribute template
114
+ * @type {AttributeTemplate}
115
+ */
116
+ const defaultAttributeTemplate = {
117
+ context: '<saml:Attribute Name="{Name}" NameFormat="{NameFormat}"><saml:AttributeValue xmlns:xs="{ValueXmlnsXs}" xmlns:xsi="{ValueXmlnsXsi}" xsi:type="{ValueXsiType}">{Value}</saml:AttributeValue></saml:Attribute>',
118
+ };
119
+ /**
120
+ * @desc Default login response template
121
+ * @type {LoginResponseTemplate}
122
+ */
123
+ const defaultLoginResponseTemplate = {
124
+ context: '<samlp:Response xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" InResponseTo="{InResponseTo}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="{StatusCode}"/></samlp:Status><saml:Assertion xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{AssertionID}" Version="2.0" IssueInstant="{IssueInstant}"><saml:Issuer>{Issuer}</saml:Issuer><saml:Subject><saml:NameID Format="{NameIDFormat}">{NameID}</saml:NameID><saml:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer"><saml:SubjectConfirmationData NotOnOrAfter="{SubjectConfirmationDataNotOnOrAfter}" Recipient="{SubjectRecipient}" InResponseTo="{InResponseTo}"/></saml:SubjectConfirmation></saml:Subject><saml:Conditions NotBefore="{ConditionsNotBefore}" NotOnOrAfter="{ConditionsNotOnOrAfter}"><saml:AudienceRestriction><saml:Audience>{Audience}</saml:Audience></saml:AudienceRestriction></saml:Conditions>{AuthnStatement}{AttributeStatement}</saml:Assertion></samlp:Response>',
125
+ attributes: [],
126
+ additionalTemplates: {
127
+ 'attributeStatementTemplate': defaultAttributeStatementTemplate,
128
+ 'attributeTemplate': defaultAttributeTemplate
129
+ }
130
+ };
131
+ /**
132
+ * @desc Default logout response template
133
+ * @type {LogoutResponseTemplate}
134
+ */
135
+ const defaultLogoutResponseTemplate = {
136
+ context: '<samlp:LogoutResponse xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="{ID}" Version="2.0" IssueInstant="{IssueInstant}" Destination="{Destination}" InResponseTo="{InResponseTo}"><saml:Issuer>{Issuer}</saml:Issuer><samlp:Status><samlp:StatusCode Value="{StatusCode}"/></samlp:Status></samlp:LogoutResponse>',
137
+ };
138
+ function getSigningSchemeForNode(sigAlg) {
139
+ if (sigAlg) {
140
+ const algAlias = nrsaAliasMappingForNode[sigAlg];
141
+ if (!(algAlias === undefined)) {
142
+ return algAlias;
143
+ }
144
+ }
145
+ return nrsaAliasMappingForNode[signatureAlgorithms.RSA_SHA1];
146
+ }
147
+ /**
148
+ * @private
149
+ * @desc Get the digest algorithms by signature algorithms
150
+ * @param {string} sigAlg signature algorithm
151
+ * @return {string/undefined} digest algorithm
152
+ */
153
+ function getDigestMethod(sigAlg) {
154
+ return digestAlgorithms[sigAlg];
155
+ }
156
+ /**
157
+ * @public
158
+ * @desc Create XPath
159
+ * @param {string/object} local parameters to create XPath
160
+ * @param {boolean} isExtractAll define whether returns whole content according to the XPath
161
+ * @return {string} xpath
162
+ */
163
+ function createXPath(local, isExtractAll) {
164
+ if ((0, utility_js_1.isString)(local)) {
165
+ return isExtractAll === true ? "//*[local-name(.)='" + local + "']/text()" : "//*[local-name(.)='" + local + "']";
166
+ }
167
+ return "//*[local-name(.)='" + local.name + "']/@" + local.attr;
168
+ }
169
+ /**
170
+ * @private
171
+ * @desc Tag normalization
172
+ * @param {string} prefix prefix of the tag
173
+ * @param {content} content normalize it to capitalized camel case
174
+ * @return {string}
175
+ */
176
+ function tagging(prefix, content) {
177
+ const camelContent = (0, camelcase_1.default)(content, { locale: 'en-us' });
178
+ return prefix + camelContent.charAt(0).toUpperCase() + camelContent.slice(1);
179
+ }
180
+ function escapeTag(replacement) {
181
+ return (_match, quote) => {
182
+ const text = (replacement === null || replacement === undefined) ? '' : String(replacement);
183
+ // not having a quote means this interpolation isn't for an attribute, and so does not need escaping
184
+ return quote ? `${quote}${(0, xml_escape_1.default)(text)}` : text;
185
+ };
186
+ }
187
+ return {
188
+ createXPath,
189
+ getQueryParamByType,
190
+ defaultLoginRequestTemplate,
191
+ defaultLoginResponseTemplate,
192
+ defaultAttributeStatementTemplate,
193
+ defaultAttributeTemplate,
194
+ defaultLogoutRequestTemplate,
195
+ defaultLogoutResponseTemplate,
196
+ /**
197
+ * @desc Replace the tag (e.g. {tag}) inside the raw XML
198
+ * @param {string} rawXML raw XML string used to do keyword replacement
199
+ * @param {array} tagValues tag values
200
+ * @return {string}
201
+ */
202
+ replaceTagsByValue(rawXML, tagValues) {
203
+ Object.keys(tagValues).forEach(t => {
204
+ rawXML = rawXML.replace(new RegExp(`("?)\\{${t}\\}`, 'g'), escapeTag(tagValues[t]));
205
+ });
206
+ return rawXML;
207
+ },
208
+ /**
209
+ * @desc Helper function to build the AttributeStatement tag
210
+ * @param {LoginResponseAttribute} attributes an array of attribute configuration
211
+ * @param {AttributeTemplate} attributeTemplate the attribute tag template to be used
212
+ * @param {AttributeStatementTemplate} attributeStatementTemplate the attributeStatement tag template to be used
213
+ * @return {string}
214
+ */
215
+ attributeStatementBuilder(attributes, attributeTemplate = defaultAttributeTemplate, attributeStatementTemplate = defaultAttributeStatementTemplate) {
216
+ const attr = attributes.map(({ name, nameFormat, valueTag, valueXsiType, type, valueXmlnsXs, valueXmlnsXsi }) => {
217
+ const defaultValueXmlnsXs = 'http://www.w3.org/2001/XMLSchema';
218
+ const defaultValueXmlnsXsi = 'http://www.w3.org/2001/XMLSchema-instance';
219
+ let attributeLine = attributeTemplate.context;
220
+ if (attributeLine && typeof attributeLine === 'function') {
221
+ // 安全调用
222
+ // @ts-ignore
223
+ return attributeLine({ name, nameFormat, valueTag, valueXsiType, type, valueXmlnsXs: valueXmlnsXs ?? defaultValueXmlnsXs, valueXmlnsXsi: valueXmlnsXsi ?? defaultValueXmlnsXsi });
224
+ }
225
+ else {
226
+ attributeLine = attributeLine.replace('{Name}', name);
227
+ attributeLine = attributeLine.replace('{NameFormat}', nameFormat);
228
+ attributeLine = attributeLine.replace('{ValueXmlnsXs}', valueXmlnsXs ? valueXmlnsXs : defaultValueXmlnsXs);
229
+ attributeLine = attributeLine.replace('{ValueXmlnsXsi}', valueXmlnsXsi ? valueXmlnsXsi : defaultValueXmlnsXsi);
230
+ attributeLine = attributeLine.replace('{ValueXsiType}', valueXsiType);
231
+ attributeLine = attributeLine.replace('{Value}', `{${tagging('attr', valueTag)}}`);
232
+ return attributeLine;
233
+ }
234
+ }).join('');
235
+ return attributeStatementTemplate.context.replace('{Attributes}', attr);
236
+ },
237
+ /**
238
+ * @desc Construct the XML signature for POST binding
239
+ * @param {string} rawSamlMessage request/response xml string
240
+ * @param {string} referenceTagXPath reference uri
241
+ * @param {string} privateKey declares the private key
242
+ * @param {string} passphrase passphrase of the private key [optional]
243
+ * @param {string|buffer} signingCert signing certificate
244
+ * @param {string} signatureAlgorithm signature algorithm
245
+ * @param {string[]} transformationAlgorithms canonicalization and transformation Algorithms
246
+ * @return {string} base64 encoded string
247
+ */
248
+ constructSAMLSignature(opts) {
249
+ const { rawSamlMessage, referenceTagXPath, privateKey, privateKeyPass, signatureAlgorithm = signatureAlgorithms.RSA_SHA512, transformationAlgorithms = [
250
+ 'http://www.w3.org/2000/09/xmldsig#enveloped-signature',
251
+ 'http://www.w3.org/2001/10/xml-exc-c14n#',
252
+ ], signingCert, signatureConfig, isBase64Output = true, isMessageSigned = false, } = opts;
253
+ const sig = new xml_crypto_1.SignedXml();
254
+ // Add assertion sections as reference
255
+ const digestAlgorithm = getDigestMethod(signatureAlgorithm);
256
+ if (referenceTagXPath) {
257
+ sig.addReference({
258
+ xpath: referenceTagXPath,
259
+ transforms: transformationAlgorithms,
260
+ digestAlgorithm: digestAlgorithm
261
+ });
262
+ }
263
+ if (isMessageSigned) {
264
+ sig.addReference({
265
+ // reference to the root node
266
+ xpath: '/*',
267
+ transforms: transformationAlgorithms,
268
+ digestAlgorithm
269
+ });
270
+ }
271
+ sig.signatureAlgorithm = signatureAlgorithm;
272
+ sig.publicCert = this.getKeyInfo(signingCert, signatureConfig).getKey();
273
+ sig.getKeyInfoContent = this.getKeyInfo(signingCert, signatureConfig).getKeyInfo;
274
+ sig.privateKey = utility_js_1.default.readPrivateKey(privateKey, privateKeyPass, true);
275
+ sig.canonicalizationAlgorithm = 'http://www.w3.org/2001/10/xml-exc-c14n#';
276
+ if (signatureConfig) {
277
+ sig.computeSignature(rawSamlMessage, signatureConfig);
278
+ }
279
+ else {
280
+ sig.computeSignature(rawSamlMessage);
281
+ }
282
+ return isBase64Output !== false ? utility_js_1.default.base64Encode(sig.getSignedXml()) : sig.getSignedXml();
283
+ },
284
+ /**
285
+ * @desc Verify the XML signature
286
+ * @param {string} xml xml
287
+ * @param {SignatureVerifierOptions} opts cert declares the X509 certificate
288
+ * @return {[boolean, string | null]} - A tuple where:
289
+ * - The first element is `true` if the signature is valid, `false` otherwise.
290
+ * - The second element is the cryptographically authenticated assertion node as a string, or `null` if not found.
291
+ */
292
+ verifySignature(xml, opts) {
293
+ const { dom } = (0, api_js_1.getContext)();
294
+ const doc = dom.parseFromString(xml, 'application/xml');
295
+ const docParser = new xmldom_1.DOMParser();
296
+ // In order to avoid the wrapping attack, we have changed to use absolute xpath instead of naively fetching the signature element
297
+ // message signature (logout response / saml response)
298
+ const messageSignatureXpath = "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Signature']";
299
+ // assertion signature (logout response / saml response)
300
+ const assertionSignatureXpath = "/*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/*[local-name(.)='Assertion']/*[local-name(.)='Signature']";
301
+ // check if there is a potential malicious wrapping signature
302
+ const wrappingElementsXPath = "/*[contains(local-name(), 'Response')]/*[local-name(.)='Assertion']/*[local-name(.)='Subject']/*[local-name(.)='SubjectConfirmation']/*[local-name(.)='SubjectConfirmationData']//*[local-name(.)='Assertion' or local-name(.)='Signature']";
303
+ // select the signature node
304
+ let selection = [];
305
+ const messageSignatureNode = (0, xpath_1.select)(messageSignatureXpath, doc);
306
+ const assertionSignatureNode = (0, xpath_1.select)(assertionSignatureXpath, doc);
307
+ const wrappingElementNode = (0, xpath_1.select)(wrappingElementsXPath, doc);
308
+ selection = selection.concat(messageSignatureNode);
309
+ selection = selection.concat(assertionSignatureNode);
310
+ // try to catch potential wrapping attack
311
+ // @ts-expect-error misssing Node properties are not needed
312
+ if (wrappingElementNode.length !== 0) {
313
+ throw new Error('ERR_POTENTIAL_WRAPPING_ATTACK');
314
+ }
315
+ // guarantee to have a signature in saml response
316
+ if (selection.length === 0) {
317
+ throw new Error('ERR_ZERO_SIGNATURE');
318
+ }
319
+ // need to refactor later on
320
+ for (const signatureNode of selection) {
321
+ const sig = new xml_crypto_1.SignedXml();
322
+ let verified = false;
323
+ sig.signatureAlgorithm = opts.signatureAlgorithm;
324
+ if (!opts.keyFile && !opts.metadata) {
325
+ throw new Error('ERR_UNDEFINED_SIGNATURE_VERIFIER_OPTIONS');
326
+ }
327
+ if (opts.keyFile) {
328
+ sig.publicCert = fs.readFileSync(opts.keyFile);
329
+ }
330
+ if (opts.metadata) {
331
+ const certificateNode = (0, xpath_1.select)(".//*[local-name(.)='X509Certificate']", signatureNode);
332
+ // certificate in metadata
333
+ let metadataCert = opts.metadata.getX509Certificate(certUse.signing);
334
+ // flattens the nested array of Certificates from each KeyDescriptor
335
+ if (Array.isArray(metadataCert)) {
336
+ metadataCert = (0, utility_js_1.flattenDeep)(metadataCert);
337
+ }
338
+ else if (typeof metadataCert === 'string') {
339
+ metadataCert = [metadataCert];
340
+ }
341
+ // normalise the certificate string
342
+ metadataCert = metadataCert.map(utility_js_1.default.normalizeCerString);
343
+ // no certificate in node response nor metadata
344
+ if (certificateNode.length === 0 && metadataCert.length === 0) {
345
+ throw new Error('NO_SELECTED_CERTIFICATE');
346
+ }
347
+ // certificate node in response
348
+ if (certificateNode.length !== 0) {
349
+ const x509CertificateData = certificateNode[0].firstChild.data;
350
+ const x509Certificate = utility_js_1.default.normalizeCerString(x509CertificateData);
351
+ if (metadataCert.length >= 1 &&
352
+ !metadataCert.find(cert => cert.trim() === x509Certificate.trim())) {
353
+ // keep this restriction for rolling certificate usage
354
+ // to make sure the response certificate is one of those specified in metadata
355
+ throw new Error('ERROR_UNMATCH_CERTIFICATE_DECLARATION_IN_METADATA');
356
+ }
357
+ sig.publicCert = this.getKeyInfo(x509Certificate).getKey();
358
+ }
359
+ else {
360
+ // Select first one from metadata
361
+ sig.publicCert = this.getKeyInfo(metadataCert[0]).getKey();
362
+ }
363
+ }
364
+ sig.loadSignature(signatureNode);
365
+ doc.removeChild(signatureNode);
366
+ verified = sig.checkSignature(doc.toString());
367
+ // immediately throw error when any one of the signature is failed to get verified
368
+ if (!verified) {
369
+ throw new Error('ERR_FAILED_TO_VERIFY_SIGNATURE');
370
+ }
371
+ // attempt is made to get the signed Reference as a string();
372
+ // note, we don't have access to the actual signedReferences API unfortunately
373
+ // mainly a sanity check here for SAML. (Although ours would still be secure, if multiple references are used)
374
+ if (!(sig.getSignedReferences().length >= 1)) {
375
+ throw new Error('NO_SIGNATURE_REFERENCES');
376
+ }
377
+ const signedVerifiedXML = sig.getSignedReferences()[0];
378
+ const rootNode = docParser.parseFromString(signedVerifiedXML, 'text/xml').documentElement;
379
+ // process the verified signature:
380
+ // case 1, rootSignedDoc is a response:
381
+ if (rootNode?.localName === 'Response') {
382
+ // try getting the Xml from the first assertion
383
+ const EncryptedAssertions = (0, xpath_1.select)("./*[local-name()='EncryptedAssertion']", rootNode);
384
+ const assertions = (0, xpath_1.select)("./*[local-name()='Assertion']", rootNode);
385
+ // now we can process the assertion as an assertion
386
+ // @ts-expect-error misssing Node properties are not needed
387
+ if (EncryptedAssertions.length === 1) {
388
+ // @ts-expect-error misssing Node properties are not needed
389
+ return [true, EncryptedAssertions[0].toString()];
390
+ }
391
+ // @ts-expect-error misssing Node properties are not needed
392
+ if (assertions.length === 1) {
393
+ // @ts-expect-error misssing Node properties are not needed
394
+ return [true, assertions[0].toString()];
395
+ }
396
+ }
397
+ else if (rootNode?.localName === 'Assertion') {
398
+ return [true, rootNode.toString()];
399
+ }
400
+ else {
401
+ return [true, null]; // signature is valid. But there is no assertion node here. It could be metadata node, hence return null
402
+ }
403
+ }
404
+ ;
405
+ // something has gone seriously wrong if we are still here
406
+ throw new Error('ERR_ZERO_SIGNATURE');
407
+ // response must be signed, either entire document or assertion
408
+ // default we will take the assertion section under root
409
+ /* if (messageSignatureNode.length === 1) {
410
+ const node = select("/!*[contains(local-name(), 'Response') or contains(local-name(), 'Request')]/!*[local-name(.)='Assertion']", doc);
411
+ if (node.length === 1) {
412
+ assertionNode = node[0].toString();
413
+ }
414
+ }
415
+
416
+ if (assertionSignatureNode.length === 1) {
417
+ const verifiedAssertionInfo = extract(assertionSignatureNode[0].toString(), [{
418
+ key: 'refURI',
419
+ localPath: ['Signature', 'SignedInfo', 'Reference'],
420
+ attributes: ['URI']
421
+ }]);
422
+ // get the assertion supposed to be the one should be verified
423
+ const desiredAssertionInfo = extract(doc.toString(), [{
424
+ key: 'id',
425
+ localPath: ['~Response', 'Assertion'],
426
+ attributes: ['ID']
427
+ }]);
428
+ // 5.4.2 References
429
+ // SAML assertions and protocol messages MUST supply a value for the ID attribute on the root element of
430
+ // the assertion or protocol message being signed. The assertion’s or protocol message's root element may
431
+ // or may not be the root element of the actual XML document containing the signed assertion or protocol
432
+ // message (e.g., it might be contained within a SOAP envelope).
433
+ // Signatures MUST contain a single <ds:Reference> containing a same-document reference to the ID
434
+ // attribute value of the root element of the assertion or protocol message being signed. For example, if the
435
+ // ID attribute value is "foo", then the URI attribute in the <ds:Reference> element MUST be "#foo".
436
+ if (verifiedAssertionInfo.refURI !== `#${desiredAssertionInfo.id}`) {
437
+ throw new Error('ERR_POTENTIAL_WRAPPING_ATTACK');
438
+ }
439
+ const verifiedDoc = extract(doc.toString(), [{
440
+ key: 'assertion',
441
+ localPath: ['~Response', 'Assertion'],
442
+ attributes: [],
443
+ context: true
444
+ }]);
445
+ assertionNode = verifiedDoc.assertion.toString();
446
+ }
447
+
448
+ return [verified, assertionNode];*/
449
+ },
450
+ /**
451
+ * @desc Helper function to create the key section in metadata (abstraction for signing and encrypt use)
452
+ * @param {string} use type of certificate (e.g. signing, encrypt)
453
+ * @param {string} certString declares the certificate String
454
+ * @return {object} object used in xml module
455
+ */
456
+ createKeySection(use, certString) {
457
+ return {
458
+ ['KeyDescriptor']: [
459
+ {
460
+ _attr: { use },
461
+ },
462
+ {
463
+ ['ds:KeyInfo']: [
464
+ {
465
+ _attr: {
466
+ 'xmlns:ds': 'http://www.w3.org/2000/09/xmldsig#',
467
+ },
468
+ },
469
+ {
470
+ ['ds:X509Data']: [{
471
+ 'ds:X509Certificate': utility_js_1.default.normalizeCerString(certString),
472
+ }],
473
+ },
474
+ ],
475
+ }
476
+ ],
477
+ };
478
+ },
479
+ /**
480
+ * SAML 消息签名 (符合 SAML V2.0 绑定规范)
481
+ * @param octetString - 要签名的原始数据 (OCTET STRING)
482
+ * @param key - PEM 格式私钥
483
+ * @param passphrase - 私钥密码 (如果有加密)
484
+ * @param isBase64 - 是否返回 base64 编码 (默认 true)
485
+ * @param signingAlgorithm - 签名算法 (默认 'rsa-sha256')
486
+ * @returns 消息签名
487
+ */
488
+ constructMessageSignature(octetString, key, passphrase, isBase64 = true, signingAlgorithm = nrsaAliasMappingForNode[signatureAlgorithms.RSA_SHA1]) {
489
+ try {
490
+ // 1. 标准化输入数据
491
+ const inputData = Buffer.isBuffer(octetString)
492
+ ? octetString
493
+ : Buffer.from(octetString, 'utf8');
494
+ // 2. 创建签名器并设置算
495
+ const signingAlgorithmValue = getSigningSchemeForNode(signingAlgorithm);
496
+ const signer = (0, node_crypto_1.createSign)(signingAlgorithmValue);
497
+ // 3. 加载私钥
498
+ const privateKey = (0, node_crypto_1.createPrivateKey)({
499
+ key: key,
500
+ format: 'pem',
501
+ passphrase: passphrase,
502
+ encoding: 'utf8'
503
+ });
504
+ signer.write(octetString);
505
+ signer.end();
506
+ const signature = signer.sign(privateKey, 'base64');
507
+ console.log(signature.toString());
508
+ console.log('dayingyixia');
509
+ // 5. 处理编码输出
510
+ return isBase64 ? signature.toString() : signature;
511
+ }
512
+ catch (error) {
513
+ throw new Error(`SAML 签名失败: ${error.message}`);
514
+ }
515
+ },
516
+ verifyMessageSignature(metadata, octetString, signature, verifyAlgorithm) {
517
+ const signCert = metadata.getX509Certificate(certUse.signing);
518
+ const signingScheme = getSigningSchemeForNode(verifyAlgorithm);
519
+ const verifier = (0, node_crypto_1.createVerify)(signingScheme);
520
+ verifier.update(octetString);
521
+ const isValid = verifier.verify(utility_js_1.default.getPublicKeyPemFromCertificate(signCert), Buffer.isBuffer(signature) ? signature : Buffer.from(signature, 'base64'));
522
+ console.log(isValid);
523
+ console.log('验证结果-------------');
524
+ return isValid;
525
+ },
526
+ /**
527
+ * @desc Get the public key in string format
528
+ * @param {string} x509Certificate certificate
529
+ * @return {string} public key
530
+ */
531
+ getKeyInfo(x509Certificate, signatureConfig = {}) {
532
+ const prefix = signatureConfig.prefix ? `${signatureConfig.prefix}:` : '';
533
+ return {
534
+ getKeyInfo: () => {
535
+ return `<${prefix}X509Data><${prefix}X509Certificate>${x509Certificate}</${prefix}X509Certificate></${prefix}X509Data>`;
536
+ },
537
+ getKey: () => {
538
+ return utility_js_1.default.getPublicKeyPemFromCertificate(x509Certificate).toString();
539
+ },
540
+ };
541
+ },
542
+ /**
543
+ * @desc Encrypt the assertion section in Response
544
+ * @param {Entity} sourceEntity source entity
545
+ * @param {Entity} targetEntity target entity
546
+ * @param {string} xml response in xml string format
547
+ * @return {Promise} a promise to resolve the finalized xml
548
+ */
549
+ encryptAssertion(sourceEntity, targetEntity, xml) {
550
+ // Implement encryption after signature if it has
551
+ return new Promise((resolve, reject) => {
552
+ if (!xml) {
553
+ return reject(new Error('ERR_UNDEFINED_ASSERTION'));
554
+ }
555
+ const sourceEntitySetting = sourceEntity.entitySetting;
556
+ const targetEntityMetadata = targetEntity.entityMeta;
557
+ const { dom } = (0, api_js_1.getContext)();
558
+ const doc = dom.parseFromString(xml, 'application/xml');
559
+ const assertions = (0, xpath_1.select)("//*[local-name(.)='Assertion']", doc);
560
+ if (!Array.isArray(assertions) || assertions.length === 0) {
561
+ throw new Error('ERR_NO_ASSERTION');
562
+ }
563
+ if (assertions.length > 1) {
564
+ throw new Error('ERR_MULTIPLE_ASSERTION');
565
+ }
566
+ const rawAssertionNode = assertions[0];
567
+ // Perform encryption depends on the setting, default is false
568
+ if (sourceEntitySetting.isAssertionEncrypted) {
569
+ const publicKeyPem = utility_js_1.default.getPublicKeyPemFromCertificate(targetEntityMetadata.getX509Certificate(certUse.encrypt));
570
+ xmlenc.encrypt(rawAssertionNode.toString(), {
571
+ // use xml-encryption module
572
+ rsa_pub: Buffer.from(publicKeyPem),
573
+ pem: Buffer.from(`-----BEGIN CERTIFICATE-----${targetEntityMetadata.getX509Certificate(certUse.encrypt)}-----END CERTIFICATE-----`),
574
+ encryptionAlgorithm: sourceEntitySetting.dataEncryptionAlgorithm,
575
+ keyEncryptionAlgorithm: sourceEntitySetting.keyEncryptionAlgorithm,
576
+ keyEncryptionDigest: 'SHA-512',
577
+ disallowEncryptionWithInsecureAlgorithm: true,
578
+ warnInsecureAlgorithm: true
579
+ }, (err, res) => {
580
+ if (err) {
581
+ console.error(err);
582
+ return reject(new Error('ERR_EXCEPTION_OF_ASSERTION_ENCRYPTION'));
583
+ }
584
+ if (!res) {
585
+ return reject(new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION'));
586
+ }
587
+ const { encryptedAssertion: encAssertionPrefix } = sourceEntitySetting.tagPrefix;
588
+ const encryptAssertionDoc = dom.parseFromString(`<${encAssertionPrefix}:EncryptedAssertion xmlns:${encAssertionPrefix}="${urn_js_1.namespace.names.assertion}">${res}</${encAssertionPrefix}:EncryptedAssertion>`, 'application/xml');
589
+ doc?.replaceChild(encryptAssertionDoc.documentElement, rawAssertionNode);
590
+ return resolve(utility_js_1.default.base64Encode(doc.toString()));
591
+ });
592
+ }
593
+ else {
594
+ return resolve(utility_js_1.default.base64Encode(xml)); // No need to do encryption
595
+ }
596
+ });
597
+ },
598
+ /**
599
+ * @desc Decrypt the assertion section in Response
600
+ * @param {string} type only accept SAMLResponse to proceed decryption
601
+ * @param {Entity} here this entity
602
+ * @param {Entity} from from the entity where the message is sent
603
+ * @param {string} entireXML response in xml string format
604
+ * @return {function} a promise to get back the entire xml with decrypted assertion
605
+ */
606
+ decryptAssertion(here, entireXML) {
607
+ return new Promise((resolve, reject) => {
608
+ // Implement decryption first then check the signature
609
+ if (!entireXML) {
610
+ return reject(new Error('ERR_UNDEFINED_ASSERTION'));
611
+ }
612
+ // Perform encryption depends on the setting of where the message is sent, default is false
613
+ const hereSetting = here.entitySetting;
614
+ const { dom } = (0, api_js_1.getContext)();
615
+ const doc = dom.parseFromString(entireXML, 'application/xml');
616
+ const encryptedAssertions = (0, xpath_1.select)("/*[contains(local-name(), 'Response')]/*[local-name(.)='EncryptedAssertion']", doc);
617
+ if (!Array.isArray(encryptedAssertions) || encryptedAssertions.length === 0) {
618
+ throw new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION');
619
+ }
620
+ if (encryptedAssertions.length > 1) {
621
+ throw new Error('ERR_MULTIPLE_ASSERTION');
622
+ }
623
+ const encAssertionNode = encryptedAssertions[0];
624
+ return xmlenc.decrypt(encAssertionNode.toString(), {
625
+ key: utility_js_1.default.readPrivateKey(hereSetting.encPrivateKey, hereSetting.encPrivateKeyPass),
626
+ }, (err, res) => {
627
+ if (err) {
628
+ console.error(err);
629
+ return reject(new Error('ERR_EXCEPTION_OF_ASSERTION_DECRYPTION'));
630
+ }
631
+ if (!res) {
632
+ return reject(new Error('ERR_UNDEFINED_ENCRYPTED_ASSERTION'));
633
+ }
634
+ const rawAssertionDoc = dom.parseFromString(res, 'application/xml');
635
+ doc?.replaceChild(rawAssertionDoc?.documentElement, encAssertionNode);
636
+ return resolve([doc.toString(), res]);
637
+ });
638
+ });
639
+ },
640
+ /**
641
+ * @desc Check if the xml string is valid and bounded
642
+ */
643
+ async isValidXml(input) {
644
+ // check if global api contains the validate function
645
+ const { validate } = (0, api_js_1.getContext)();
646
+ /**
647
+ * user can write a validate function that always returns
648
+ * a resolved promise and skip the validator even in
649
+ * production, user will take the responsibility if
650
+ * they intend to skip the validation
651
+ */
652
+ if (!validate) {
653
+ // otherwise, an error will be thrown
654
+ return Promise.reject('Your application is potentially vulnerable because no validation function found. Please read the documentation on how to setup the validator. (https://github.com/tngan/samlify#installation)');
655
+ }
656
+ try {
657
+ return await validate(input);
658
+ }
659
+ catch (e) {
660
+ throw e;
661
+ }
662
+ },
663
+ };
664
+ };
665
+ exports.default = libSaml();
642
666
  //# sourceMappingURL=libsaml.js.map