samlesa 2.16.6 → 2.17.0

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.
package/README.md CHANGED
@@ -1,64 +1,44 @@
1
- # samlify · [![构建状态](https://img.shields.io/circleci/build/github/tngan/samlify?style=for-the-badge&logo=circleci)](https://app.circleci.com/pipelines/github/tngan/samlify) [![npm 版本](https://img.shields.io/npm/v/samlify.svg?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/samlify) [![下载量](https://img.shields.io/npm/dm/samlify.svg?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/samlify) [![覆盖率](https://img.shields.io/coveralls/tngan/samlify/master.svg?style=for-the-badge&logo=coveralls)](https://coveralls.io/github/tngan/samlify?branch=master)
2
-
3
- 高度可配置的 Node.js SAML 2.0 单点登录库
4
- Highly configurable Node.js SAML 2.0 library for Single Sign On
1
+ # samlify · [![构建状态](https://img.shields.io/circleci/build/github/tngan/samlify?style=for-the-badge&logo=circleci)](https://app.circleci.com/pipelines/github/tngan/samlify) [![npm 版本](https://img.shields.io/npm/v/samlify.svg?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/samlify) [![下载量](https://img.shields.io/npm/dm/samlify.svg?style=for-the-badge&logo=npm)](https://www.npmjs.com/package/samlify) [![覆盖率](https://img.shields.io/coveralls/tngan/samlify/master.svg?style=for-the-badge&logo=coveralls)](https://coveralls.io/github/tngan/samlify?branch=master)
5
2
 
6
3
  ---
7
-
8
- ## 🔄 本仓库为 [samlify](https://github.com/tngan/samlify) 的改进分支版本,原作者[tngan](https://github.com/tngan)
9
-
10
- ### 主要改进 / Key Improvements
11
-
12
- - 📦 CJS模块打包转为 ESModule
13
-
14
- - ✅ 将依赖包 `@authenio/xml-encryption` 替换为 `xml-encryption` 并升级版本对 sha256/512 加密密钥 OAEP 摘要方法的支持
15
-
16
- - 🛠️ 修复加密断言验证签名函数 verifySignature 提取`Assertion` 字段的错误,增加对加密断言 `EncryptedAssertion` 字段提取逻辑
17
-
18
- - 📦 ServiceProvider实例化函数 attributeConsumingService字段参函数, 生成默认的 `AttributeConsumingService` 元素和属性值
19
-
20
- - 🗑️ 移除作为Idp使用 IdentityProvider 函数自定义函数模板loginResponseTemplate字段的支持,并改进了自定义函数替换。
21
- 改进createLoginResponse函数签名改为对象的传参方式
22
-
23
- - 🔒 默认签名算法升级为 SHA-256,Idp默认加密算法为 AES_256_GCM
24
-
25
- - ⬆️ 升级所有能够升级的依赖版本,移除 `node-rsa`/`node-forge` 模块儿,改用原生nodejs `crypto` 模块实现。
26
-
27
- - 🌐 将 `url` 库替换为 `URL` 原生 API
28
- - 改进了如果响应为的绑定`urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect`,某些情况下未能DEFLATE压缩导致不能提取xml的异常情况的处理
29
- - 现在如果遇到加密响应无需显示传递 `isAssertionEncrypted` 字段,也无需传递 `MessageSignatureOrder`
30
- 字段。因为我认为是否加密应该是可以自动判断的,MessageSignatureOrder我修改了判断逻辑并在Keycloak 验证可以通过。使用前你应该自行验证这其中的风险
31
- - 默认 elementsOrder 增加了 AttributeConsumingService 适配
32
- - 我已经使用 Burp SAML Raider测试了 八种XSW都能良好的应对,以及XXE。你应该自行验证
4
+ [English Version](#README.md) | [中文版本](#readmeCN.md)
5
+ ## 🔄 This repository is an improved fork of [samlify](https://github.com/tngan/samlify) by [tngan](https://github.com/tngan)
6
+
7
+ ### Key Improvements
8
+
9
+ - 📦 Converted from CJS to ESModule
10
+ - ✅ Replaced `@authenio/xml-encryption` with `xml-encryption` and added support for sha256/512 encryption key OAEP digest methods
11
+ - ✅ Upgraded `@xmldom/xmldom` to the latest version
12
+ - 🛠️ Fixed encrypted assertion signature verification by adding `EncryptedAssertion` field extraction logic
13
+ - 📦 Added default `AttributeConsumingService` element generation for ServiceProvider
14
+ - 📦 Added partial Artifact binding support
15
+ - 🗑️ Removed custom template support for IdentityProvider and improved parameter passing
16
+ - 🔒 Upgraded default signature algorithm to SHA-256 and default encryption to AES_256_GCM
17
+ - 🧪 Added built-in XML XSD validator
18
+ - 🐛 Improved handling of HTTP-Redirect binding without DEFLATE compression
19
+ - 🔓 Automatic detection of encrypted assertions without explicit flags
20
+ - 📝 Added AttributeConsumingService to default elementsOrder
21
+ - ✅ Tested against Burp SAML Raider (XSW and XXE attacks)
22
+ - Migrated tests to Vitest
33
23
 
34
24
  ---
35
25
 
36
- ## 欢迎 PR / Welcome PRs
26
+ ## Welcome PRs
37
27
 
38
- 欢迎贡献代码或提供与其他框架集成的用例
39
- Welcome contributions or integration examples with frameworks
28
+ Contributions are welcome! Please feel free to submit pull requests or provide integration examples with other frameworks.
40
29
 
41
30
  ---
42
31
 
43
- ## 安装 / Installation
44
- 您应该在使用的前提下首先设置验证其
45
- ```js
32
+ ## How to use?
46
33
 
47
- import * as validator from '@authenio/samlify-xsd-schema-validator';
48
- import * as Saml from "samlesa";
49
- import {Extractor,} from "samlesa";
50
- import validator from '@authenio/samlify-node-xmllint'
51
- // 设置模式验证器 / Set schema validator
52
- Saml.setSchemaValidator(validator);
34
+ Refer to the `type/flows.test.ts` test cases and the original documentation at [https://samlify.js.org](https://samlify.js.org). Note that some parameters have been changed in this fork.
53
35
 
36
+ ---
54
37
 
55
- ```
56
-
57
- ## 生成密钥
58
-
59
- 我们使用 openssl 生成密钥和证书用于测试。私钥可以使用密码保护,这是可选的。以下是生成私钥和自签名证书的命令。
38
+ ## Generating Keys
60
39
 
61
- > openssl genrsa -passout pass:foobar -out encryptKey.pem 4096
62
- > openssl req -new -x509 -key encryptKey.pem -out encryptionCert.cer -days 3650
40
+ Use OpenSSL to generate keys and certificates for testing. Private keys can be password-protected (optional). Here are the commands:
63
41
 
64
- #
42
+ ```bash
43
+ openssl genrsa -passout pass:foobar -out encryptKey.pem 4096
44
+ openssl req -new -x509 -key encryptKey.pem -out encryptionCert.cer -days 3650
@@ -1,18 +1,18 @@
1
1
  /**
2
- * @file binding-post.ts
3
- * @author tngan
4
- * @desc Binding-level API, declare the functions using POST binding
5
- */
2
+ * @file binding-post.ts
3
+ * @author tngan
4
+ * @desc Binding-level API, declare the functions using POST binding
5
+ */
6
6
  import { wording, StatusCode } from './urn.js';
7
7
  import libsaml from './libsaml.js';
8
8
  import utility, { get } from './utility.js';
9
9
  const binding = wording.binding;
10
10
  /**
11
- * @desc Generate a base64 encoded login request
12
- * @param {string} referenceTagXPath reference uri
13
- * @param {object} entity object includes both idp and sp
14
- * @param {function} customTagReplacement used when developers have their own login response template
15
- */
11
+ * @desc Generate a base64 encoded login request
12
+ * @param {string} referenceTagXPath reference uri
13
+ * @param {object} entity object includes both idp and sp
14
+ * @param {function} customTagReplacement used when developers have their own login response template
15
+ */
16
16
  function base64LoginRequest(referenceTagXPath, entity, customTagReplacement) {
17
17
  const metadata = { idp: entity.idp.entityMeta, sp: entity.sp.entityMeta };
18
18
  const spSetting = entity.sp.entitySetting;
@@ -141,7 +141,6 @@ async function base64LoginResponse(requestInfo = {}, entity, user = {}, customTa
141
141
  };
142
142
  // step: sign assertion ? -> encrypted ? -> sign message ?
143
143
  if (metadata.sp.isWantAssertionsSigned()) {
144
- // console.debug('sp wants assertion signed');
145
144
  rawSamlResponse = libsaml.constructSAMLSignature({
146
145
  ...config,
147
146
  rawSamlMessage: rawSamlResponse,
@@ -149,7 +148,10 @@ async function base64LoginResponse(requestInfo = {}, entity, user = {}, customTa
149
148
  referenceTagXPath: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']",
150
149
  signatureConfig: {
151
150
  prefix: 'ds',
152
- location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']/*[local-name(.)='Issuer']", action: 'after' },
151
+ location: {
152
+ reference: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']/*[local-name(.)='Issuer']",
153
+ action: 'after'
154
+ },
153
155
  },
154
156
  });
155
157
  }
@@ -168,7 +170,19 @@ async function base64LoginResponse(requestInfo = {}, entity, user = {}, customTa
168
170
  },
169
171
  });
170
172
  }
171
- // console.debug('after message signed', rawSamlResponse);
173
+ /* if (spSetting.wantMessageSigned) {
174
+ // console.debug('sign then encrypt and sign entire message');
175
+ rawSamlResponse = libsaml.constructSAMLSignature({
176
+ ...config,
177
+ rawSamlMessage: rawSamlResponse,
178
+ isMessageSigned: true,
179
+ transformationAlgorithms: spSetting.transformationAlgorithms,
180
+ signatureConfig: spSetting.signatureConfig || {
181
+ prefix: 'ds',
182
+ location: {reference: "/!*[local-name(.)='Response']/!*[local-name(.)='Issuer']", action: 'after'},
183
+ },
184
+ });
185
+ }*/
172
186
  if (idpSetting.isAssertionEncrypted) {
173
187
  // console.debug('idp is configured to do encryption');
174
188
  const context = await libsaml.encryptAssertion(entity.idp, entity.sp, rawSamlResponse);
@@ -181,18 +195,18 @@ async function base64LoginResponse(requestInfo = {}, entity, user = {}, customTa
181
195
  }
182
196
  }
183
197
  //sign after encrypting
184
- if (encryptThenSign && (spSetting.wantMessageSigned || !metadata.sp.isWantAssertionsSigned())) {
185
- rawSamlResponse = libsaml.constructSAMLSignature({
198
+ /* if (encryptThenSign && (spSetting.wantMessageSigned || !metadata.sp.isWantAssertionsSigned())) {
199
+ rawSamlResponse = libsaml.constructSAMLSignature({
186
200
  ...config,
187
201
  rawSamlMessage: rawSamlResponse,
188
202
  isMessageSigned: true,
189
203
  transformationAlgorithms: spSetting.transformationAlgorithms,
190
204
  signatureConfig: spSetting.signatureConfig || {
191
- prefix: 'ds',
192
- location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Issuer']", action: 'after' },
205
+ prefix: 'ds',
206
+ location: {reference: "/!*[local-name(.)='Response']/!*[local-name(.)='Issuer']", action: 'after'},
193
207
  },
194
- });
195
- }
208
+ });
209
+ }*/
196
210
  return Promise.resolve({
197
211
  id,
198
212
  context: utility.base64Encode(rawSamlResponse),
@@ -201,13 +215,13 @@ async function base64LoginResponse(requestInfo = {}, entity, user = {}, customTa
201
215
  throw new Error('ERR_GENERATE_POST_LOGIN_RESPONSE_MISSING_METADATA');
202
216
  }
203
217
  /**
204
- * @desc Generate a base64 encoded logout request
205
- * @param {object} user current logged user (e.g. req.user)
206
- * @param {string} referenceTagXPath reference uri
207
- * @param {object} entity object includes both idp and sp
208
- * @param {function} customTagReplacement used when developers have their own login response template
209
- * @return {string} base64 encoded request
210
- */
218
+ * @desc Generate a base64 encoded logout request
219
+ * @param {object} user current logged user (e.g. req.user)
220
+ * @param {string} referenceTagXPath reference uri
221
+ * @param {object} entity object includes both idp and sp
222
+ * @param {function} customTagReplacement used when developers have their own login response template
223
+ * @return {string} base64 encoded request
224
+ */
211
225
  function base64LogoutRequest(user, referenceTagXPath, entity, customTagReplacement) {
212
226
  const metadata = { init: entity.init.entityMeta, target: entity.target.entityMeta };
213
227
  const initSetting = entity.init.entitySetting;
@@ -262,12 +276,12 @@ function base64LogoutRequest(user, referenceTagXPath, entity, customTagReplaceme
262
276
  throw new Error('ERR_GENERATE_POST_LOGOUT_REQUEST_MISSING_METADATA');
263
277
  }
264
278
  /**
265
- * @desc Generate a base64 encoded logout response
266
- * @param {object} requestInfo corresponding request, used to obtain the id
267
- * @param {string} referenceTagXPath reference uri
268
- * @param {object} entity object includes both idp and sp
269
- * @param {function} customTagReplacement used when developers have their own login response template
270
- */
279
+ * @desc Generate a base64 encoded logout response
280
+ * @param {object} requestInfo corresponding request, used to obtain the id
281
+ * @param {string} referenceTagXPath reference uri
282
+ * @param {object} entity object includes both idp and sp
283
+ * @param {function} customTagReplacement used when developers have their own login response template
284
+ */
271
285
  function base64LogoutResponse(requestInfo, entity, customTagReplacement) {
272
286
  const metadata = {
273
287
  init: entity.init.entityMeta,
@@ -135,8 +135,6 @@ function loginRequestRedirectURLArt(entity, customTagReplacement) {
135
135
  AllowCreate: spSetting.allowCreate,
136
136
  });
137
137
  }
138
- console.log(rawSamlRequest);
139
- console.log("-----------------这是原始请求模板-------------------");
140
138
  const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm, transformationAlgorithms } = spSetting;
141
139
  if (metadata.idp.isWantAuthnRequestsSigned()) {
142
140
  let signAuthnRequest = libsaml.constructSAMLSignature({
@@ -156,8 +154,6 @@ function loginRequestRedirectURLArt(entity, customTagReplacement) {
156
154
  },
157
155
  }
158
156
  });
159
- console.log(signAuthnRequest);
160
- console.log("签名后的模板");
161
157
  rawSamlRequest = signAuthnRequest;
162
158
  }
163
159
  /* console.log(metadata.idp)
@@ -169,9 +165,6 @@ function loginRequestRedirectURLArt(entity, customTagReplacement) {
169
165
  Issuer: metadata.sp.getEntityID(),
170
166
  AuthnRequest: rawSamlRequest
171
167
  });
172
- console.log(soapTemplate);
173
- console.log("======================最后结果========================");
174
- console.log("======================开始签名根节点========================");
175
168
  let rootSignSoap = libsaml.constructSAMLSignature({
176
169
  isMessageSigned: true,
177
170
  isBase64Output: false,
@@ -186,8 +179,6 @@ function loginRequestRedirectURLArt(entity, customTagReplacement) {
186
179
  location: { reference: "//*[local-name()='Header']", action: 'after' },
187
180
  }
188
181
  });
189
- console.log(rootSignSoap);
190
- console.log("======================已经签名========================");
191
182
  return {
192
183
  authnRequest: rootSignSoap
193
184
  };
@@ -224,7 +215,6 @@ function loginResponseRedirectURL(requestInfo, entity, user = {}, relayState, cu
224
215
  // Five minutes later : nowtime + 5 * 60 * 1000 (in milliseconds)
225
216
  const fiveMinutesLaterTime = new Date(nowTime.getTime() + 300_000);
226
217
  const now = nowTime.toISOString();
227
- console.log(`现在是北京时间:${nowTime.toLocaleString()}`);
228
218
  const sessionIndex = 'session' + idpSetting.generateID(); // 这个是当前系统的会话索引,用于单点注销
229
219
  const tenHoursLaterTime = new Date(nowTime.getTime());
230
220
  tenHoursLaterTime.setHours(tenHoursLaterTime.getHours() + 10);
@@ -119,7 +119,6 @@ async function base64LoginResponse(requestInfo = {}, entity, user = {}, relaySta
119
119
  // Five minutes later : nowtime + 5 * 60 * 1000 (in milliseconds)
120
120
  const fiveMinutesLaterTime = new Date(nowTime.getTime() + 300_000);
121
121
  const now = nowTime.toISOString();
122
- console.log(`现在是北京时间:${nowTime.toLocaleString()}`);
123
122
  const sessionIndex = 'session' + idpSetting.generateID(); // 这个是当前系统的会话索引,用于单点注销
124
123
  const tenHoursLaterTime = new Date(nowTime.getTime());
125
124
  tenHoursLaterTime.setHours(tenHoursLaterTime.getHours() + 10);
@@ -59,11 +59,7 @@ export class IdentityProvider extends Entity {
59
59
  sp,
60
60
  }, user, relayState, customTagReplacement, AttributeStatement);
61
61
  default:
62
- context = await postBinding.base64LoginResponse(requestInfo, {
63
- idp: this,
64
- sp,
65
- }, user, customTagReplacement, encryptThenSign, AttributeStatement);
66
- /* throw new Error('ERR_CREATE_RESPONSE_UNDEFINED_BINDING');*/
62
+ throw new Error('ERR_CREATE_RESPONSE_UNDEFINED_BINDING');
67
63
  }
68
64
  return {
69
65
  ...context,
@@ -156,8 +156,6 @@ export class ServiceProvider extends Entity {
156
156
  */
157
157
  createArt(entityIDString, endpointIndex = 0) {
158
158
  let sourceEntityId = entityIDString ? entityIDString : this.entityMeta.getEntityID();
159
- console.log(sourceEntityId);
160
- console.log("0000000000000000000000000000000000000000");
161
159
  // 1. 固定类型代码 (0x0004 - 2字节)
162
160
  const typeCode = Buffer.from([0x00, 0x04]);
163
161
  // 2. 端点索引 (2字节,大端序)
@@ -191,7 +191,7 @@ export const logoutResponseFields = [
191
191
  ];
192
192
  export function extract(context, fields) {
193
193
  const { dom } = getContext();
194
- const rootDoc = dom.parseFromString(context);
194
+ const rootDoc = dom.parseFromString(context, 'application/xml');
195
195
  return fields.reduce((result, field) => {
196
196
  // get essential fields
197
197
  const key = field.key;
@@ -207,7 +207,7 @@ export function extract(context, fields) {
207
207
  // if shortcut is used, then replace the doc
208
208
  // it's a design for overriding the doc used during runtime
209
209
  if (shortcut) {
210
- targetDoc = dom.parseFromString(shortcut);
210
+ targetDoc = dom.parseFromString(shortcut, 'application/xml');
211
211
  }
212
212
  // special case: multiple path
213
213
  /*
@@ -229,6 +229,7 @@ export function extract(context, fields) {
229
229
  .join(' | ');
230
230
  return {
231
231
  ...result,
232
+ // @ts-expect-error misssing Node properties are not needed
232
233
  [key]: uniq(select(multiXPaths, targetDoc).map((n) => n.nodeValue).filter(notEmpty))
233
234
  };
234
235
  }
@@ -249,8 +250,10 @@ export function extract(context, fields) {
249
250
  // find the index in localpath
250
251
  const indexPath = buildAttributeXPath(index);
251
252
  const fullLocalXPath = `${baseXPath}${indexPath}`;
253
+ // @ts-expect-error misssing Node properties are not needed
252
254
  const parentNodes = select(baseXPath, targetDoc);
253
255
  // [uid, mail, edupersonaffiliation], ready for aggregate
256
+ // @ts-expect-error misssing Node properties are not needed
254
257
  const parentAttributes = select(fullLocalXPath, targetDoc).map((n) => n.value);
255
258
  // [attribute, attributevalue]
256
259
  const childXPath = buildAbsoluteXPath([last(localPath)].concat(attributePath));
@@ -258,8 +261,9 @@ export function extract(context, fields) {
258
261
  const fullChildXPath = `${childXPath}${childAttributeXPath}`;
259
262
  // [ 'test', 'test@example.com', [ 'users', 'examplerole1' ] ]
260
263
  const childAttributes = parentNodes.map(node => {
261
- const nodeDoc = dom.parseFromString(node.toString());
264
+ const nodeDoc = dom.parseFromString(node.toString(), 'application/xml');
262
265
  if (attributes.length === 0) {
266
+ // @ts-ignore
263
267
  const childValues = select(fullChildXPath, nodeDoc).map((n) => n.nodeValue);
264
268
  if (childValues.length === 1) {
265
269
  return childValues[0];
@@ -267,6 +271,7 @@ export function extract(context, fields) {
267
271
  return childValues;
268
272
  }
269
273
  if (attributes.length > 0) {
274
+ // @ts-ignore
270
275
  const childValues = select(fullChildXPath, nodeDoc).map((n) => n.value);
271
276
  if (childValues.length === 1) {
272
277
  return childValues[0];
@@ -292,6 +297,7 @@ export function extract(context, fields) {
292
297
  }
293
298
  */
294
299
  if (isEntire) {
300
+ // @ts-expect-error misssing Node properties are not needed
295
301
  const node = select(baseXPath, targetDoc);
296
302
  let value = null;
297
303
  if (node.length === 1) {
@@ -314,10 +320,13 @@ export function extract(context, fields) {
314
320
  }
315
321
  */
316
322
  if (attributes.length > 1) {
323
+ // @ts-ignore
317
324
  const baseNode = select(baseXPath, targetDoc).map(n => n.toString());
318
325
  const childXPath = `${buildAbsoluteXPath([last(localPath)])}${attributeXPath}`;
319
326
  const attributeValues = baseNode.map((node) => {
320
- const nodeDoc = dom.parseFromString(node);
327
+ // @ts-ignore
328
+ const nodeDoc = dom.parseFromString(node, 'application/xml');
329
+ // @ts-ignore
321
330
  const values = select(childXPath, nodeDoc).reduce((r, n) => {
322
331
  r[camelCase(n.name, { locale: 'en-us' })] = n.value;
323
332
  return r;
@@ -339,6 +348,7 @@ export function extract(context, fields) {
339
348
  */
340
349
  if (attributes.length === 1) {
341
350
  const fullPath = `${baseXPath}${attributeXPath}`;
351
+ // @ts-ignore
342
352
  const attributeValues = select(fullPath, targetDoc).map((n) => n.value);
343
353
  return {
344
354
  ...result,
@@ -355,9 +365,11 @@ export function extract(context, fields) {
355
365
  */
356
366
  if (attributes.length === 0) {
357
367
  let attributeValue = null;
368
+ // @ts-expect-error misssing Node properties are not needed
358
369
  const node = select(baseXPath, targetDoc);
359
370
  if (node.length === 1) {
360
371
  const fullPath = `string(${baseXPath}${attributeXPath})`;
372
+ // @ts-expect-error misssing Node properties are not needed
361
373
  attributeValue = select(fullPath, targetDoc);
362
374
  }
363
375
  if (node.length > 1) {
package/build/src/flow.js CHANGED
@@ -131,7 +131,7 @@ async function postFlow(options) {
131
131
  const direction = libsaml.getQueryParamByType(parserType);
132
132
  let encodedRequest = '';
133
133
  let samlContent = '';
134
- if (soap === false) {
134
+ if (!soap) {
135
135
  encodedRequest = body[direction];
136
136
  // @ts-ignore
137
137
  samlContent = String(base64Decode(encodedRequest));
@@ -186,7 +186,7 @@ async function postFlow(options) {
186
186
  let decryptRequired = from.entitySetting.isAssertionEncrypted;
187
187
  let extractorFields = [];
188
188
  // validate the xml first
189
- let res = await libsaml.isValidXml(samlContent).catch((error) => {
189
+ let res = await libsaml.isValidXml(samlContent, soap).catch((error) => {
190
190
  return Promise.reject('ERR_EXCEPTION_VALIDATE_XML');
191
191
  });
192
192
  if (res !== true) {
@@ -198,23 +198,7 @@ async function postFlow(options) {
198
198
  // check status based on different scenarios
199
199
  await checkStatus(samlContent, parserType, soap);
200
200
  /**检查签名顺序 */
201
- /* if (
202
- checkSignature &&
203
- from.entitySetting.messageSigningOrder === MessageSignatureOrder.ETS
204
- ) {
205
- console.log("===============我走的这里=========================")
206
- const [verified, verifiedAssertionNode,isDecryptRequired] = libsaml.verifySignature(samlContent, verificationOptions);
207
- console.log(verified);
208
- console.log("verified")
209
- decryptRequired = isDecryptRequired
210
- if (!verified) {
211
- return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
212
- }
213
- if (!decryptRequired) {
214
- extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
215
- }
216
- }*/
217
- if (soap === true) {
201
+ if (soap) {
218
202
  const [verified, verifiedAssertionNode, isDecryptRequired] = libsaml.verifySignatureSoap(samlContent, verificationOptions);
219
203
  decryptRequired = isDecryptRequired;
220
204
  if (!verified) {
@@ -227,7 +211,8 @@ async function postFlow(options) {
227
211
  // 1. 解密断言
228
212
  const [decryptedSAML, decryptedAssertion] = await libsaml.decryptAssertionSoap(self, samlContent);
229
213
  // 2. 检查解密后的断言是否包含签名
230
- const assertionDoc = new DOMParser().parseFromString(decryptedAssertion, 'text/xml');
214
+ const assertionDoc = new DOMParser().parseFromString(decryptedAssertion, 'application/xml');
215
+ // @ts-ignore
231
216
  const assertionSignatureNodes = select("./*[local-name()='Signature']", assertionDoc.documentElement);
232
217
  // 3. 如果存在签名则验证
233
218
  if (assertionSignatureNodes.length > 0) {
@@ -239,7 +224,6 @@ async function postFlow(options) {
239
224
  // 3.2 验证断言签名
240
225
  const [assertionVerified, result] = libsaml.verifySignatureSoap(decryptedAssertion, assertionVerificationOptions);
241
226
  if (!assertionVerified) {
242
- console.error("解密后的断言签名验证失败");
243
227
  return Promise.reject('ERR_FAIL_TO_VERIFY_ASSERTION_SIGNATURE');
244
228
  }
245
229
  if (assertionVerified) {
@@ -254,34 +238,26 @@ async function postFlow(options) {
254
238
  }
255
239
  }
256
240
  }
257
- if (soap === false) {
258
- const [verified, verifiedAssertionNode, isDecryptRequired] = libsaml.verifySignature(samlContent, verificationOptions);
241
+ if (!soap) {
242
+ const [verified, verifiedAssertionNode, isDecryptRequired, noSignature] = libsaml.verifySignature(samlContent, verificationOptions);
259
243
  decryptRequired = isDecryptRequired;
260
- if (!verified) {
244
+ if (isDecryptRequired && noSignature) {
245
+ const result = await libsaml.decryptAssertion(self, samlContent);
246
+ samlContent = result[0];
247
+ extractorFields = getDefaultExtractorFields(parserType, result[1]);
248
+ }
249
+ if (!verified && !noSignature && !isDecryptRequired) {
261
250
  return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
262
251
  }
263
252
  if (!decryptRequired) {
264
253
  extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
265
254
  }
266
- if (parserType === 'SAMLResponse' && decryptRequired) {
255
+ if (parserType === 'SAMLResponse' && decryptRequired && !noSignature) {
267
256
  const result = await libsaml.decryptAssertion(self, samlContent);
268
257
  samlContent = result[0];
269
258
  extractorFields = getDefaultExtractorFields(parserType, result[1]);
270
259
  }
271
260
  }
272
- // verify the signatures (the response is signed then encrypted, then decrypt first then verify)
273
- /* if (
274
- checkSignature &&
275
- from.entitySetting.messageSigningOrder === MessageSignatureOrder.STE
276
- ) {
277
- const [verified, verifiedAssertionNode,isDecryptRequired] = libsaml.verifySignature(samlContent, verificationOptions);
278
- decryptRequired = isDecryptRequired
279
- if (verified) {
280
- extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
281
- } else {
282
- return Promise.reject('ERR_FAIL_TO_VERIFY_STE_SIGNATURE');
283
- }
284
- }*/
285
261
  const parseResult = {
286
262
  samlContent: samlContent,
287
263
  extract: extract(samlContent, extractorFields),
@@ -351,29 +327,13 @@ async function postArtifactFlow(options) {
351
327
  let decryptRequired = from.entitySetting.isAssertionEncrypted;
352
328
  let extractorFields = [];
353
329
  // validate the xml first
354
- let res = await libsaml.isValidXml(samlContent);
330
+ let res = await libsaml.isValidXml(samlContent, true);
355
331
  if (parserType !== urlParams.samlResponse) {
356
332
  extractorFields = getDefaultExtractorFields(parserType, null);
357
333
  }
358
334
  // check status based on different scenarios
359
335
  await checkStatus(samlContent, parserType);
360
336
  /**检查签名顺序 */
361
- /* if (
362
- checkSignature &&
363
- from.entitySetting.messageSigningOrder === MessageSignatureOrder.ETS
364
- ) {
365
- console.log("===============我走的这里=========================")
366
- const [verified, verifiedAssertionNode,isDecryptRequired] = libsaml.verifySignature(samlContent, verificationOptions);
367
- console.log(verified);
368
- console.log("verified")
369
- decryptRequired = isDecryptRequired
370
- if (!verified) {
371
- return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
372
- }
373
- if (!decryptRequired) {
374
- extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
375
- }
376
- }*/
377
337
  const [verified, verifiedAssertionNode, isDecryptRequired] = libsaml.verifySignature(samlContent, verificationOptions);
378
338
  decryptRequired = isDecryptRequired;
379
339
  if (!verified) {
@@ -387,19 +347,6 @@ async function postArtifactFlow(options) {
387
347
  samlContent = result[0];
388
348
  extractorFields = getDefaultExtractorFields(parserType, result[1]);
389
349
  }
390
- // verify the signatures (the response is signed then encrypted, then decrypt first then verify)
391
- /* if (
392
- checkSignature &&
393
- from.entitySetting.messageSigningOrder === MessageSignatureOrder.STE
394
- ) {
395
- const [verified, verifiedAssertionNode,isDecryptRequired] = libsaml.verifySignature(samlContent, verificationOptions);
396
- decryptRequired = isDecryptRequired
397
- if (verified) {
398
- extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
399
- } else {
400
- return Promise.reject('ERR_FAIL_TO_VERIFY_STE_SIGNATURE');
401
- }
402
- }*/
403
350
  const parseResult = {
404
351
  samlContent: samlContent,
405
352
  extract: extract(samlContent, extractorFields),
@@ -469,7 +416,7 @@ async function postSimpleSignFlow(options) {
469
416
  const xmlString = String(base64Decode(encodedRequest));
470
417
  // validate the xml
471
418
  try {
472
- await libsaml.isValidXml(xmlString);
419
+ await libsaml.isValidXml(xmlString, false);
473
420
  }
474
421
  catch (e) {
475
422
  return Promise.reject('ERR_INVALID_XML');