samlesa 2.17.0 → 2.17.2

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.
Files changed (36) hide show
  1. package/build/index.js +3 -2
  2. package/build/src/binding-artifact.js +330 -146
  3. package/build/src/binding-post.js +2 -0
  4. package/build/src/entity-sp.js +21 -94
  5. package/build/src/extractor.js +32 -0
  6. package/build/src/flow.js +23 -112
  7. package/build/src/libsaml.js +325 -127
  8. package/build/src/libsamlSoap.js +115 -0
  9. package/build/src/metadata-idp.js +9 -9
  10. package/build/src/metadata-sp.js +6 -6
  11. package/build/src/schema/saml-schema-metadata-2.0.xsd +10 -9
  12. package/build/src/schemaValidator.js +43 -4
  13. package/build/src/soap.js +123 -3
  14. package/package.json +8 -5
  15. package/types/index.d.ts +3 -2
  16. package/types/index.d.ts.map +1 -1
  17. package/types/src/binding-artifact.d.ts +24 -29
  18. package/types/src/binding-artifact.d.ts.map +1 -1
  19. package/types/src/binding-post.d.ts.map +1 -1
  20. package/types/src/entity-sp.d.ts +13 -24
  21. package/types/src/entity-sp.d.ts.map +1 -1
  22. package/types/src/extractor.d.ts +22 -0
  23. package/types/src/extractor.d.ts.map +1 -1
  24. package/types/src/flow.d.ts +1 -0
  25. package/types/src/flow.d.ts.map +1 -1
  26. package/types/src/libsaml.d.ts +4 -3
  27. package/types/src/libsaml.d.ts.map +1 -1
  28. package/types/src/libsamlSoap.d.ts +7 -0
  29. package/types/src/libsamlSoap.d.ts.map +1 -0
  30. package/types/src/schemaValidator.d.ts +1 -0
  31. package/types/src/schemaValidator.d.ts.map +1 -1
  32. package/types/src/soap.d.ts +33 -0
  33. package/types/src/soap.d.ts.map +1 -1
  34. package/types/src/validator.d.ts.map +1 -1
  35. package/build/src/schema/XMLSchema.dtd +0 -402
  36. package/build/src/schema/datatypes.dtd +0 -203
package/build/index.js CHANGED
@@ -9,11 +9,12 @@ export { default as SamlLib } from './src/libsaml.js';
9
9
  // new name convention in version >= 3.0
10
10
  import * as Constants from './src/urn.js';
11
11
  import * as Extractor from './src/extractor.js';
12
- import { validate } from './src/schemaValidator.js';
12
+ import * as Soap from './src/soap.js';
13
+ import { validate, validateMetadata } from './src/schemaValidator.js';
13
14
  // exposed methods for customizing samlify
14
15
  import { setSchemaValidator, setDOMParserOptions } from './src/api.js';
15
16
  export { Constants, Extractor,
16
17
  // temp: resolve the conflict after version >= 3.0
17
18
  IdentityProvider, IdentityProviderInstance, ServiceProvider, ServiceProviderInstance,
18
19
  // set context
19
- setSchemaValidator, setDOMParserOptions, validate };
20
+ setSchemaValidator, setDOMParserOptions, validate, validateMetadata, Soap };
@@ -3,20 +3,57 @@
3
3
  * @author tngan
4
4
  * @desc Binding-level API, declare the functions using POST binding
5
5
  */
6
- import { wording, StatusCode } from './urn.js';
6
+ import { checkStatus } from "./flow.js";
7
+ import { ParserType, StatusCode, wording } from './urn.js';
7
8
  import libsaml from './libsaml.js';
9
+ import libsamlSoap from './libsamlSoap.js';
8
10
  import utility, { get } from './utility.js';
11
+ import { fileURLToPath } from "node:url";
12
+ import * as uuid from 'uuid';
13
+ import { artifactResolveFields, extract, loginRequestFields, loginResponseFields, logoutRequestFields, logoutResponseFields } from "./extractor.js";
14
+ import { verifyTime } from "./validator.js";
15
+ import { sendArtifactResolve } from "./soap.js";
16
+ import path from "node:path";
17
+ const __filename = fileURLToPath(import.meta.url);
18
+ const __dirname = path.dirname(__filename);
19
+ // get the default extractor fields based on the parserType
20
+ function getDefaultExtractorFields(parserType, assertion) {
21
+ switch (parserType) {
22
+ case ParserType.SAMLRequest:
23
+ return loginRequestFields;
24
+ case ParserType.SAMLResponse:
25
+ if (!assertion) {
26
+ // unexpected hit
27
+ throw new Error('ERR_EMPTY_ASSERTION');
28
+ }
29
+ return loginResponseFields(assertion);
30
+ case ParserType.LogoutRequest:
31
+ return logoutRequestFields;
32
+ case ParserType.LogoutResponse:
33
+ return logoutResponseFields;
34
+ default:
35
+ throw new Error('ERR_UNDEFINED_PARSERTYPE');
36
+ }
37
+ }
9
38
  const binding = wording.binding;
10
39
  /**
11
40
  * @desc Generate a base64 encoded login request
12
41
  * @param {string} referenceTagXPath reference uri
13
42
  * @param {object} entity object includes both idp and sp
14
- * @param {function} customTagReplacement used when developers have their own login response template
43
+ * @param customTagReplacement
15
44
  */
16
- function base64LoginRequest(referenceTagXPath, entity, customTagReplacement) {
17
- const metadata = { idp: entity.idp.entityMeta, sp: entity.sp.entityMeta };
45
+ function soapLoginRequest(referenceTagXPath, entity, customTagReplacement) {
46
+ const metadata = {
47
+ idp: entity.idp.entityMeta,
48
+ sp: entity.sp.entityMeta,
49
+ inResponse: entity?.inResponse,
50
+ relayState: entity?.relayState
51
+ };
18
52
  const spSetting = entity.sp.entitySetting;
19
53
  let id = '';
54
+ let id2 = spSetting.generateID();
55
+ let soapTemplate = '';
56
+ let Response = '';
20
57
  if (metadata && metadata.idp && metadata.sp) {
21
58
  const base = metadata.idp.getSingleSignOnService(binding.post);
22
59
  let rawSamlRequest;
@@ -40,30 +77,59 @@ function base64LoginRequest(referenceTagXPath, entity, customTagReplacement) {
40
77
  NameIDFormat: selectedNameIDFormat
41
78
  });
42
79
  }
80
+ const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm, transformationAlgorithms } = spSetting;
43
81
  if (metadata.idp.isWantAuthnRequestsSigned()) {
44
- const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm, transformationAlgorithms } = spSetting;
45
- return {
46
- id,
47
- context: libsaml.constructSAMLSignature({
48
- referenceTagXPath,
49
- privateKey,
50
- privateKeyPass,
51
- signatureAlgorithm,
52
- transformationAlgorithms,
53
- rawSamlMessage: rawSamlRequest,
54
- signingCert: metadata.sp.getX509Certificate('signing'),
55
- signatureConfig: spSetting.signatureConfig || {
56
- prefix: 'ds',
57
- location: { reference: "/*[local-name(.)='AuthnRequest']/*[local-name(.)='Issuer']", action: 'after' },
58
- }
59
- }),
60
- };
82
+ Response = libsaml.constructSAMLSignature({
83
+ referenceTagXPath,
84
+ privateKey,
85
+ privateKeyPass,
86
+ signatureAlgorithm,
87
+ transformationAlgorithms,
88
+ rawSamlMessage: rawSamlRequest,
89
+ isBase64Output: false,
90
+ signingCert: metadata.sp.getX509Certificate('signing'),
91
+ signatureConfig: spSetting.signatureConfig || {
92
+ prefix: 'ds',
93
+ location: { reference: "/*[local-name(.)='AuthnRequest']/!*[local-name(.)='Issuer']", action: 'after' },
94
+ }
95
+ });
96
+ soapTemplate = libsaml.replaceTagsByValue(libsaml.defaultArtAuthnRequestTemplate.context, {
97
+ ID: id2,
98
+ IssueInstant: new Date().toISOString(),
99
+ InResponseTo: metadata.inResponse ?? "",
100
+ Issuer: metadata.sp.getEntityID(),
101
+ AuthnRequest: Response
102
+ });
103
+ }
104
+ else {
105
+ soapTemplate = libsaml.replaceTagsByValue(libsaml.defaultArtAuthnRequestTemplate.context, {
106
+ ID: id2,
107
+ IssueInstant: new Date().toISOString(),
108
+ InResponseTo: metadata.inResponse ?? "",
109
+ Issuer: metadata.sp.getEntityID(),
110
+ AuthnRequest: rawSamlRequest
111
+ });
61
112
  }
113
+ /** 构建响应签名*/
62
114
  // No need to embeded XML signature
63
- return {
64
- id,
65
- context: utility.base64Encode(rawSamlRequest),
66
- };
115
+ return libsaml.constructSAMLSignature({
116
+ referenceTagXPath: "/*[local-name(.)='Envelope']/*[local-name(.)='Body']/*[local-name(.)='ArtifactResponse']",
117
+ privateKey,
118
+ privateKeyPass,
119
+ signatureAlgorithm,
120
+ transformationAlgorithms,
121
+ rawSamlMessage: soapTemplate,
122
+ isBase64Output: false,
123
+ isMessageSigned: false,
124
+ signingCert: metadata.sp.getX509Certificate('signing'),
125
+ signatureConfig: {
126
+ prefix: 'ds',
127
+ location: {
128
+ reference: "/*[local-name(.)='Envelope']/*[local-name(.)='Body']/*[local-name(.)='ArtifactResponse']/*[local-name(.)='Issuer']",
129
+ action: 'after'
130
+ }
131
+ },
132
+ });
67
133
  }
68
134
  throw new Error('ERR_GENERATE_POST_LOGIN_REQUEST_MISSING_METADATA');
69
135
  }
@@ -76,7 +142,7 @@ function base64LoginRequest(referenceTagXPath, entity, customTagReplacement) {
76
142
  * @param {boolean} encryptThenSign whether or not to encrypt then sign first (if signing). Defaults to sign-then-encrypt
77
143
  * @param AttributeStatement
78
144
  */
79
- async function base64LoginResponse(requestInfo = {}, entity, user = {}, customTagReplacement, encryptThenSign = false, AttributeStatement = []) {
145
+ async function soapLoginResponse(requestInfo = {}, entity, user = {}, customTagReplacement, encryptThenSign = false, AttributeStatement = []) {
80
146
  const idpSetting = entity.idp.entitySetting;
81
147
  const spSetting = entity.sp.entitySetting;
82
148
  const id = idpSetting.generateID();
@@ -141,7 +207,6 @@ async function base64LoginResponse(requestInfo = {}, entity, user = {}, customTa
141
207
  };
142
208
  // step: sign assertion ? -> encrypted ? -> sign message ?
143
209
  if (metadata.sp.isWantAssertionsSigned()) {
144
- // console.debug('sp wants assertion signed');
145
210
  rawSamlResponse = libsaml.constructSAMLSignature({
146
211
  ...config,
147
212
  rawSamlMessage: rawSamlResponse,
@@ -149,7 +214,10 @@ async function base64LoginResponse(requestInfo = {}, entity, user = {}, customTa
149
214
  referenceTagXPath: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']",
150
215
  signatureConfig: {
151
216
  prefix: 'ds',
152
- location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']/*[local-name(.)='Issuer']", action: 'after' },
217
+ location: {
218
+ reference: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']/*[local-name(.)='Issuer']",
219
+ action: 'after'
220
+ },
153
221
  },
154
222
  });
155
223
  }
@@ -200,134 +268,250 @@ async function base64LoginResponse(requestInfo = {}, entity, user = {}, customTa
200
268
  }
201
269
  throw new Error('ERR_GENERATE_POST_LOGIN_RESPONSE_MISSING_METADATA');
202
270
  }
203
- /**
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
- */
211
- function base64LogoutRequest(user, referenceTagXPath, entity, customTagReplacement) {
212
- const metadata = { init: entity.init.entityMeta, target: entity.target.entityMeta };
213
- const initSetting = entity.init.entitySetting;
214
- const nameIDFormat = initSetting.nameIDFormat;
215
- const selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
216
- let id = '';
217
- if (metadata && metadata.init && metadata.target) {
218
- let rawSamlRequest;
219
- if (initSetting.logoutRequestTemplate && customTagReplacement) {
220
- const template = customTagReplacement(initSetting.logoutRequestTemplate.context);
221
- id = get(template, 'id', null);
222
- rawSamlRequest = get(template, 'context', null);
223
- }
224
- else {
225
- id = initSetting.generateID();
226
- const tvalue = {
227
- ID: id,
228
- Destination: metadata.target.getSingleLogoutService(binding.post),
229
- Issuer: metadata.init.getEntityID(),
230
- IssueInstant: new Date().toISOString(),
231
- EntityID: metadata.init.getEntityID(),
232
- NameIDFormat: selectedNameIDFormat,
233
- NameID: user.NameID || '',
234
- };
235
- rawSamlRequest = libsaml.replaceTagsByValue(libsaml.defaultLogoutRequestTemplate.context, tvalue);
236
- }
237
- if (entity.target.entitySetting.wantLogoutRequestSigned) {
238
- // Need to embeded XML signature
239
- const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm, transformationAlgorithms } = initSetting;
240
- return {
241
- id,
242
- context: libsaml.constructSAMLSignature({
243
- referenceTagXPath,
244
- privateKey,
245
- privateKeyPass,
246
- signatureAlgorithm,
247
- transformationAlgorithms,
248
- rawSamlMessage: rawSamlRequest,
249
- signingCert: metadata.init.getX509Certificate('signing'),
250
- signatureConfig: initSetting.signatureConfig || {
251
- prefix: 'ds',
252
- location: { reference: "/*[local-name(.)='LogoutRequest']/*[local-name(.)='Issuer']", action: 'after' },
253
- }
254
- }),
255
- };
256
- }
257
- return {
258
- id,
259
- context: utility.base64Encode(rawSamlRequest),
260
- };
271
+ async function parseLoginRequestResolve(params) {
272
+ let { idp, sp, xml, } = params;
273
+ const verificationOptions = {
274
+ metadata: idp.entityMeta,
275
+ signatureAlgorithm: idp.entitySetting.requestSignatureAlgorithm,
276
+ };
277
+ let res = await libsaml.isValidXml(xml, true).catch((error) => {
278
+ return Promise.reject('ERR_EXCEPTION_VALIDATE_XML');
279
+ });
280
+ if (res !== true) {
281
+ return Promise.reject('ERR_EXCEPTION_VALIDATE_XML');
261
282
  }
262
- throw new Error('ERR_GENERATE_POST_LOGOUT_REQUEST_MISSING_METADATA');
283
+ /** 首先先验证签名*/
284
+ // @ts-ignore
285
+ let [verify, xmlString, isEncrypted, noSignature] = await libsamlSoap.verifyAndDecryptSoapMessage(xml, verificationOptions);
286
+ if (!verify) {
287
+ return Promise.reject('ERR_FAIL_TO_VERIFY_SIGNATURE');
288
+ }
289
+ const parseResult = {
290
+ samlContent: xmlString,
291
+ extract: extract(xmlString, artifactResolveFields),
292
+ };
293
+ /**
294
+ * Validation part: validate the context of response after signature is verified and decrypted (optional)
295
+ */
296
+ const targetEntityMetadata = sp.entityMeta;
297
+ const issuer = targetEntityMetadata.getEntityID();
298
+ const extractedProperties = parseResult.extract;
299
+ // unmatched issuer
300
+ if (extractedProperties.issuer !== issuer) {
301
+ return Promise.reject('ERR_UNMATCH_ISSUER');
302
+ }
303
+ // invalid session time
304
+ // only run the verifyTime when `SessionNotOnOrAfter` exists
305
+ if (!verifyTime(undefined, new Date(new Date(extractedProperties.request.issueInstant).getTime() + 5 * 60 * 1000).toISOString(), sp.entitySetting.clockDrifts)) {
306
+ return Promise.reject('ERR_EXPIRED_SESSION');
307
+ }
308
+ return Promise.resolve(parseResult);
263
309
  }
264
- /**
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
- */
271
- function base64LogoutResponse(requestInfo, entity, customTagReplacement) {
310
+ async function parseLoginResponseResolve(params) {
311
+ let { idp, sp, art } = params;
272
312
  const metadata = {
273
- init: entity.init.entityMeta,
274
- target: entity.target.entityMeta,
313
+ idp: idp.entityMeta,
314
+ sp: sp.entityMeta,
275
315
  };
276
- let id = '';
277
- const initSetting = entity.init.entitySetting;
278
- if (metadata && metadata.init && metadata.target) {
279
- let rawSamlResponse;
280
- if (initSetting.logoutResponseTemplate) {
281
- const template = customTagReplacement(initSetting.logoutResponseTemplate.context);
282
- id = template.id;
283
- rawSamlResponse = template.context;
316
+ const verificationOptions = {
317
+ metadata: idp.entityMeta,
318
+ signatureAlgorithm: idp.entitySetting.requestSignatureAlgorithm,
319
+ };
320
+ let parserType = 'SAMLResponse';
321
+ /** 断言是否加密应根据响应里面的字段判断*/
322
+ let decryptRequired = idp.entitySetting.isAssertionEncrypted;
323
+ let extractorFields = [];
324
+ let samlContent = '';
325
+ const spSetting = sp.entitySetting;
326
+ let ID = '_' + uuid.v4();
327
+ let url = metadata.idp.getArtifactResolutionService('soap');
328
+ let samlSoapRaw = libsaml.replaceTagsByValue(libsaml.defaultArtifactResolveTemplate.context, {
329
+ ID: ID,
330
+ Destination: url,
331
+ Issuer: metadata.sp.getEntityID(),
332
+ IssueInstant: new Date().toISOString(),
333
+ Art: art
334
+ });
335
+ if (!metadata.idp.isWantAuthnRequestsSigned()) {
336
+ samlContent = await sendArtifactResolve(url, samlSoapRaw);
337
+ // check status based on different scenarios
338
+ // validate the xml
339
+ try {
340
+ await libsaml.isValidXml(samlContent, true);
284
341
  }
285
- else {
286
- id = initSetting.generateID();
287
- const tvalue = {
288
- ID: id,
289
- Destination: metadata.target.getSingleLogoutService(binding.post),
290
- EntityID: metadata.init.getEntityID(),
291
- Issuer: metadata.init.getEntityID(),
292
- IssueInstant: new Date().toISOString(),
293
- StatusCode: StatusCode.Success,
294
- InResponseTo: get(requestInfo, 'extract.request.id', '')
295
- };
296
- rawSamlResponse = libsaml.replaceTagsByValue(libsaml.defaultLogoutResponseTemplate.context, tvalue);
342
+ catch (e) {
343
+ return Promise.reject('ERR_INVALID_XML');
297
344
  }
298
- if (entity.target.entitySetting.wantLogoutResponseSigned) {
299
- const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm, transformationAlgorithms } = initSetting;
300
- return {
301
- id,
302
- context: libsaml.constructSAMLSignature({
303
- isMessageSigned: true,
304
- transformationAlgorithms: transformationAlgorithms,
305
- privateKey,
306
- privateKeyPass,
307
- signatureAlgorithm,
308
- rawSamlMessage: rawSamlResponse,
309
- signingCert: metadata.init.getX509Certificate('signing'),
310
- signatureConfig: {
311
- prefix: 'ds',
312
- location: {
313
- reference: "/*[local-name(.)='LogoutResponse']/*[local-name(.)='Issuer']",
314
- action: 'after'
315
- }
316
- }
317
- }),
318
- };
345
+ await checkStatus(samlContent, parserType, true);
346
+ }
347
+ if (metadata.idp.isWantAuthnRequestsSigned()) {
348
+ const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm, transformationAlgorithms } = spSetting;
349
+ //@ts-ignore
350
+ let signatureSoap = libsaml.constructSAMLSignature({
351
+ referenceTagXPath: "//*[local-name(.)='ArtifactResolve']",
352
+ isMessageSigned: false,
353
+ isBase64Output: false,
354
+ transformationAlgorithms: transformationAlgorithms,
355
+ //@ts-ignore
356
+ privateKey,
357
+ privateKeyPass,
358
+ //@ts-ignore
359
+ signatureAlgorithm,
360
+ rawSamlMessage: samlSoapRaw,
361
+ signingCert: metadata.sp.getX509Certificate('signing'),
362
+ signatureConfig: {
363
+ prefix: 'ds',
364
+ location: {
365
+ reference: "//*[local-name(.)='Issuer']",
366
+ action: 'after'
367
+ }
368
+ }
369
+ });
370
+ samlContent = await sendArtifactResolve(url, signatureSoap);
371
+ // check status based on different scenarios
372
+ // validate the xml
373
+ try {
374
+ await libsaml.isValidXml(samlContent, true);
319
375
  }
320
- return {
321
- id,
322
- context: utility.base64Encode(rawSamlResponse),
376
+ catch (e) {
377
+ return Promise.reject('ERR_INVALID_XML');
378
+ }
379
+ await checkStatus(samlContent, parserType, true);
380
+ const [verified1, verifiedAssertionNode1, isDecryptRequired1, noSignature1] = await libsamlSoap.verifyAndDecryptSoapMessage(samlContent, verificationOptions);
381
+ /* decryptRequired = isDecryptRequired*/
382
+ if (!verified1) {
383
+ return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
384
+ }
385
+ samlContent = verifiedAssertionNode1;
386
+ const [verified, verifiedAssertionNode, isDecryptRequired, noSignature] = libsaml.verifySignature(samlContent, verificationOptions);
387
+ if (isDecryptRequired && noSignature) {
388
+ const result = await libsaml.decryptAssertion(sp, samlContent);
389
+ samlContent = result[0];
390
+ extractorFields = getDefaultExtractorFields(parserType, result[1]);
391
+ }
392
+ if (!verified && !noSignature && !isDecryptRequired) {
393
+ return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
394
+ }
395
+ if (!isDecryptRequired) {
396
+ extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
397
+ }
398
+ if (parserType === 'SAMLResponse' && isDecryptRequired && !noSignature) {
399
+ const result = await libsaml.decryptAssertion(sp, samlContent);
400
+ samlContent = result[0];
401
+ extractorFields = getDefaultExtractorFields(parserType, result[1]);
402
+ }
403
+ const parseResult = {
404
+ samlContent: samlContent,
405
+ extract: extract(samlContent, extractorFields),
323
406
  };
407
+ /**
408
+ * Validation part: validate the context of response after signature is verified and decrypted (optional)
409
+ */
410
+ const targetEntityMetadata = idp.entityMeta;
411
+ const issuer = targetEntityMetadata.getEntityID();
412
+ const extractedProperties = parseResult.extract;
413
+ // unmatched issuer
414
+ if ((parserType === 'LogoutResponse' || parserType === 'SAMLResponse')
415
+ && extractedProperties
416
+ && extractedProperties.issuer !== issuer) {
417
+ return Promise.reject('ERR_UNMATCH_ISSUER');
418
+ }
419
+ // invalid session time
420
+ // only run the verifyTime when `SessionNotOnOrAfter` exists
421
+ if (parserType === 'SAMLResponse'
422
+ && extractedProperties.sessionIndex.sessionNotOnOrAfter
423
+ && !verifyTime(undefined, extractedProperties.sessionIndex.sessionNotOnOrAfter, sp.entitySetting.clockDrifts)) {
424
+ return Promise.reject('ERR_EXPIRED_SESSION');
425
+ }
426
+ // invalid time
427
+ // 2.4.1.2 https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
428
+ if (parserType === 'SAMLResponse'
429
+ && extractedProperties.conditions
430
+ && !verifyTime(extractedProperties.conditions.notBefore, extractedProperties.conditions.notOnOrAfter, sp.entitySetting.clockDrifts)) {
431
+ return Promise.reject('ERR_SUBJECT_UNCONFIRMED');
432
+ }
433
+ //valid destination
434
+ //There is no validation of the response here. The upper-layer application
435
+ // should verify the result by itself to see if the destination is equal to the SP acs and
436
+ // whether the response.id is used to prevent replay attacks.
437
+ /*
438
+ let destination = extractedProperties?.response?.destination
439
+ let isExit = self.entitySetting?.assertionConsumerService?.filter((item) => {
440
+ return item?.Location === destination
441
+ })
442
+ if (isExit?.length === 0) {
443
+ return Promise.reject('ERR_Destination_URL');
444
+ }
445
+ if (parserType === 'SAMLResponse') {
446
+ let destination = extractedProperties?.response?.destination
447
+ let isExit = self.entitySetting?.assertionConsumerService?.filter((item: { Location: any; }) => {
448
+ return item?.Location === destination
449
+ })
450
+ if (isExit?.length === 0) {
451
+ return Promise.reject('ERR_Destination_URL');
452
+ }
453
+ }
454
+ */
455
+ return Promise.resolve(parseResult);
456
+ }
457
+ const parseResult = {
458
+ samlContent: samlContent,
459
+ extract: extract(samlContent, extractorFields),
460
+ };
461
+ /**
462
+ * Validation part: validate the context of response after signature is verified and decrypted (optional)
463
+ */
464
+ const targetEntityMetadata = idp.entityMeta;
465
+ const issuer = targetEntityMetadata.getEntityID();
466
+ const extractedProperties = parseResult.extract;
467
+ // unmatched issuer
468
+ if ((parserType === 'LogoutResponse' || parserType === 'SAMLResponse')
469
+ && extractedProperties
470
+ && extractedProperties.issuer !== issuer) {
471
+ return Promise.reject('ERR_UNMATCH_ISSUER');
324
472
  }
325
- throw new Error('ERR_GENERATE_POST_LOGOUT_RESPONSE_MISSING_METADATA');
473
+ // invalid session time
474
+ // only run the verifyTime when `SessionNotOnOrAfter` exists
475
+ if (parserType === 'SAMLResponse'
476
+ && extractedProperties.sessionIndex.sessionNotOnOrAfter
477
+ && !verifyTime(undefined, extractedProperties.sessionIndex.sessionNotOnOrAfter, sp.entitySetting.clockDrifts)) {
478
+ return Promise.reject('ERR_EXPIRED_SESSION');
479
+ }
480
+ // invalid time
481
+ // 2.4.1.2 https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
482
+ if (parserType === 'SAMLResponse'
483
+ && extractedProperties.conditions
484
+ && !verifyTime(extractedProperties.conditions.notBefore, extractedProperties.conditions.notOnOrAfter, sp.entitySetting.clockDrifts)) {
485
+ return Promise.reject('ERR_SUBJECT_UNCONFIRMED');
486
+ }
487
+ //valid destination
488
+ //There is no validation of the response here. The upper-layer application
489
+ // should verify the result by itself to see if the destination is equal to the SP acs and
490
+ // whether the response.id is used to prevent replay attacks.
491
+ /*
492
+ let destination = extractedProperties?.response?.destination
493
+ let isExit = self.entitySetting?.assertionConsumerService?.filter((item) => {
494
+ return item?.Location === destination
495
+ })
496
+ if (isExit?.length === 0) {
497
+ return Promise.reject('ERR_Destination_URL');
498
+ }
499
+ if (parserType === 'SAMLResponse') {
500
+ let destination = extractedProperties?.response?.destination
501
+ let isExit = self.entitySetting?.assertionConsumerService?.filter((item: { Location: any; }) => {
502
+ return item?.Location === destination
503
+ })
504
+ if (isExit?.length === 0) {
505
+ return Promise.reject('ERR_Destination_URL');
506
+ }
507
+ }
508
+ */
509
+ return Promise.resolve(parseResult);
326
510
  }
327
511
  const artifactSignBinding = {
328
- base64LoginRequest,
329
- base64LoginResponse,
330
- base64LogoutRequest,
331
- base64LogoutResponse,
512
+ parseLoginRequestResolve,
513
+ soapLoginRequest,
514
+ parseLoginResponseResolve,
515
+ soapLoginResponse,
332
516
  };
333
517
  export default artifactSignBinding;
@@ -130,6 +130,8 @@ async function base64LoginResponse(requestInfo = {}, entity, user = {}, customTa
130
130
  tvalue.InResponseTo = requestInfo?.extract?.request?.id ?? '';
131
131
  }
132
132
  rawSamlResponse = libsaml.replaceTagsByValue(libsaml.defaultLoginResponseTemplate.context, tvalue);
133
+ console.log(rawSamlResponse);
134
+ console.log("没有加密签名过的------------------------------------");
133
135
  }
134
136
  const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm } = idpSetting;
135
137
  const config = {