samlesa 2.12.113 → 2.14.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.

Potentially problematic release.


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

Files changed (145) hide show
  1. package/build/index.js +18 -54
  2. package/build/index.js.map +1 -1
  3. package/build/src/api.js +18 -24
  4. package/build/src/api.js.map +1 -1
  5. package/build/src/binding-post.js +337 -365
  6. package/build/src/binding-post.js.map +1 -1
  7. package/build/src/binding-redirect.js +312 -340
  8. package/build/src/binding-redirect.js.map +1 -1
  9. package/build/src/binding-simplesign.js +201 -229
  10. package/build/src/binding-simplesign.js.map +1 -1
  11. package/build/src/entity-idp.js +119 -127
  12. package/build/src/entity-idp.js.map +1 -1
  13. package/build/src/entity-sp.js +88 -96
  14. package/build/src/entity-sp.js.map +1 -1
  15. package/build/src/entity.js +193 -225
  16. package/build/src/entity.js.map +1 -1
  17. package/build/src/extractor.js +361 -369
  18. package/build/src/extractor.js.map +1 -1
  19. package/build/src/flow.js +313 -320
  20. package/build/src/flow.js.map +1 -1
  21. package/build/src/libsaml.js +693 -721
  22. package/build/src/libsaml.js.map +1 -1
  23. package/build/src/metadata-idp.js +119 -127
  24. package/build/src/metadata-idp.js.map +1 -1
  25. package/build/src/metadata-sp.js +223 -231
  26. package/build/src/metadata-sp.js.map +1 -1
  27. package/build/src/metadata.js +138 -166
  28. package/build/src/metadata.js.map +1 -1
  29. package/build/src/types.js +4 -11
  30. package/build/src/types.js.map +1 -1
  31. package/build/src/urn.js +204 -212
  32. package/build/src/urn.js.map +1 -1
  33. package/build/src/utility.js +277 -292
  34. package/build/src/utility.js.map +1 -1
  35. package/build/src/validator.js +24 -27
  36. package/build/src/validator.js.map +1 -1
  37. package/package.json +13 -7
  38. package/types/api.d.ts +15 -0
  39. package/types/api.d.ts.map +1 -0
  40. package/types/binding-post.d.ts +48 -0
  41. package/types/binding-post.d.ts.map +1 -0
  42. package/types/binding-redirect.d.ts +54 -0
  43. package/types/binding-redirect.d.ts.map +1 -0
  44. package/types/binding-simplesign.d.ts +41 -0
  45. package/types/binding-simplesign.d.ts.map +1 -0
  46. package/types/entity-idp.d.ts +38 -0
  47. package/types/entity-idp.d.ts.map +1 -0
  48. package/types/entity-sp.d.ts +38 -0
  49. package/types/entity-sp.d.ts.map +1 -0
  50. package/types/entity.d.ts +100 -0
  51. package/types/entity.d.ts.map +1 -0
  52. package/types/extractor.d.ts +26 -0
  53. package/types/extractor.d.ts.map +1 -0
  54. package/types/flow.d.ts +7 -0
  55. package/types/flow.d.ts.map +1 -0
  56. package/types/index.d.ts +11 -10
  57. package/types/index.d.ts.map +1 -0
  58. package/types/libsaml.d.ts +208 -0
  59. package/types/libsaml.d.ts.map +1 -0
  60. package/types/metadata-idp.d.ts +25 -0
  61. package/types/metadata-idp.d.ts.map +1 -0
  62. package/types/metadata-sp.d.ts +37 -0
  63. package/types/metadata-sp.d.ts.map +1 -0
  64. package/types/metadata.d.ts +58 -0
  65. package/types/metadata.d.ts.map +1 -0
  66. package/types/src/api.d.ts +15 -13
  67. package/types/src/api.d.ts.map +1 -0
  68. package/types/src/binding-post.d.ts +48 -47
  69. package/types/src/binding-post.d.ts.map +1 -0
  70. package/types/src/binding-redirect.d.ts +54 -53
  71. package/types/src/binding-redirect.d.ts.map +1 -0
  72. package/types/src/binding-simplesign.d.ts +41 -40
  73. package/types/src/binding-simplesign.d.ts.map +1 -0
  74. package/types/src/entity-idp.d.ts +38 -37
  75. package/types/src/entity-idp.d.ts.map +1 -0
  76. package/types/src/entity-sp.d.ts +38 -36
  77. package/types/src/entity-sp.d.ts.map +1 -0
  78. package/types/src/entity.d.ts +100 -101
  79. package/types/src/entity.d.ts.map +1 -0
  80. package/types/src/extractor.d.ts +26 -25
  81. package/types/src/extractor.d.ts.map +1 -0
  82. package/types/src/flow.d.ts +7 -6
  83. package/types/src/flow.d.ts.map +1 -0
  84. package/types/src/libsaml.d.ts +208 -209
  85. package/types/src/libsaml.d.ts.map +1 -0
  86. package/types/src/metadata-idp.d.ts +25 -24
  87. package/types/src/metadata-idp.d.ts.map +1 -0
  88. package/types/src/metadata-sp.d.ts +37 -36
  89. package/types/src/metadata-sp.d.ts.map +1 -0
  90. package/types/src/metadata.d.ts +58 -59
  91. package/types/src/metadata.d.ts.map +1 -0
  92. package/types/src/types.d.ts +128 -129
  93. package/types/src/types.d.ts.map +1 -0
  94. package/types/src/urn.d.ts +195 -194
  95. package/types/src/urn.d.ts.map +1 -0
  96. package/types/src/utility.d.ts +133 -134
  97. package/types/src/utility.d.ts.map +1 -0
  98. package/types/src/validator.d.ts +4 -3
  99. package/types/src/validator.d.ts.map +1 -0
  100. package/types/types.d.ts +128 -0
  101. package/types/types.d.ts.map +1 -0
  102. package/types/urn.d.ts +195 -0
  103. package/types/urn.d.ts.map +1 -0
  104. package/types/utility.d.ts +133 -0
  105. package/types/utility.d.ts.map +1 -0
  106. package/types/validator.d.ts +4 -0
  107. package/types/validator.d.ts.map +1 -0
  108. package/.editorconfig +0 -19
  109. package/.github/FUNDING.yml +0 -1
  110. package/.idea/inspectionProfiles/Project_Default.xml +0 -6
  111. package/.idea/modules.xml +0 -8
  112. package/.idea/samlify.iml +0 -12
  113. package/.idea/vcs.xml +0 -6
  114. package/.pre-commit.sh +0 -15
  115. package/.snyk +0 -8
  116. package/.travis.yml +0 -29
  117. package/Makefile +0 -25
  118. package/index.d.ts +0 -10
  119. package/index.js +0 -19
  120. package/index.js.map +0 -1
  121. package/index.ts +0 -28
  122. package/qodana.yaml +0 -29
  123. package/src/.idea/modules.xml +0 -8
  124. package/src/.idea/src.iml +0 -12
  125. package/src/.idea/vcs.xml +0 -6
  126. package/src/api.ts +0 -36
  127. package/src/binding-post.ts +0 -348
  128. package/src/binding-redirect.ts +0 -356
  129. package/src/binding-simplesign.ts +0 -238
  130. package/src/entity-idp.ts +0 -153
  131. package/src/entity-sp.ts +0 -114
  132. package/src/entity.ts +0 -243
  133. package/src/extractor.ts +0 -392
  134. package/src/flow.ts +0 -467
  135. package/src/libsaml.ts +0 -895
  136. package/src/metadata-idp.ts +0 -146
  137. package/src/metadata-sp.ts +0 -268
  138. package/src/metadata.ts +0 -166
  139. package/src/types.ts +0 -153
  140. package/src/urn.ts +0 -211
  141. package/src/utility.ts +0 -319
  142. package/src/validator.ts +0 -39
  143. package/tsconfig.json +0 -38
  144. package/tslint.json +0 -35
  145. package/types.d.ts +0 -2
package/build/src/flow.js CHANGED
@@ -1,321 +1,314 @@
1
- "use strict";
2
- var __importDefault = (this && this.__importDefault) || function (mod) {
3
- return (mod && mod.__esModule) ? mod : { "default": mod };
4
- };
5
- Object.defineProperty(exports, "__esModule", { value: true });
6
- exports.flow = void 0;
7
- const utility_js_1 = require("./utility.js");
8
- const validator_js_1 = require("./validator.js");
9
- const libsaml_js_1 = __importDefault(require("./libsaml.js"));
10
- const extractor_js_1 = require("./extractor.js");
11
- const urn_js_1 = require("./urn.js");
12
- const bindDict = urn_js_1.wording.binding;
13
- const urlParams = urn_js_1.wording.urlParams;
14
- // get the default extractor fields based on the parserType
15
- function getDefaultExtractorFields(parserType, assertion) {
16
- switch (parserType) {
17
- case urn_js_1.ParserType.SAMLRequest:
18
- return extractor_js_1.loginRequestFields;
19
- case urn_js_1.ParserType.SAMLResponse:
20
- if (!assertion) {
21
- // unexpected hit
22
- throw new Error('ERR_EMPTY_ASSERTION');
23
- }
24
- return (0, extractor_js_1.loginResponseFields)(assertion);
25
- case urn_js_1.ParserType.LogoutRequest:
26
- return extractor_js_1.logoutRequestFields;
27
- case urn_js_1.ParserType.LogoutResponse:
28
- return extractor_js_1.logoutResponseFields;
29
- default:
30
- throw new Error('ERR_UNDEFINED_PARSERTYPE');
31
- }
32
- }
33
- // proceed the redirect binding flow
34
- async function redirectFlow(options) {
35
- const { request, parserType, self, checkSignature = true, from } = options;
36
- const { query, octetString } = request;
37
- const { SigAlg: sigAlg, Signature: signature } = query;
38
- const targetEntityMetadata = from.entityMeta;
39
- // ?SAMLRequest= or ?SAMLResponse=
40
- const direction = libsaml_js_1.default.getQueryParamByType(parserType);
41
- const content = query[direction];
42
- // query must contain the saml content
43
- if (content === undefined) {
44
- return Promise.reject('ERR_REDIRECT_FLOW_BAD_ARGS');
45
- }
46
- const xmlString = (0, utility_js_1.inflateString)(decodeURIComponent(content));
47
- // validate the xml
48
- try {
49
- await libsaml_js_1.default.isValidXml(xmlString);
50
- }
51
- catch (e) {
52
- return Promise.reject('ERR_INVALID_XML');
53
- }
54
- // check status based on different scenarios
55
- await checkStatus(xmlString, parserType);
56
- let assertion = '';
57
- if (parserType === urlParams.samlResponse) {
58
- // Extract assertion shortcut
59
- const verifiedDoc = (0, extractor_js_1.extract)(xmlString, [{
60
- key: 'assertion',
61
- localPath: ['~Response', 'Assertion'],
62
- attributes: [],
63
- context: true
64
- }]);
65
- if (verifiedDoc && verifiedDoc.assertion) {
66
- assertion = verifiedDoc.assertion;
67
- }
68
- }
69
- const extractorFields = getDefaultExtractorFields(parserType, assertion.length > 0 ? assertion : null);
70
- const parseResult = {
71
- samlContent: xmlString,
72
- sigAlg: null,
73
- extract: (0, extractor_js_1.extract)(xmlString, extractorFields),
74
- };
75
- // see if signature check is required
76
- // only verify message signature is enough
77
- if (checkSignature) {
78
- if (!signature || !sigAlg) {
79
- return Promise.reject('ERR_MISSING_SIG_ALG');
80
- }
81
- // put the below two assignments into verifyMessageSignature function
82
- const base64Signature = Buffer.from(decodeURIComponent(signature), 'base64');
83
- const decodeSigAlg = decodeURIComponent(sigAlg);
84
- const verified = libsaml_js_1.default.verifyMessageSignature(targetEntityMetadata, octetString, base64Signature, sigAlg);
85
- if (!verified) {
86
- // Fail to verify message signature
87
- return Promise.reject('ERR_FAILED_MESSAGE_SIGNATURE_VERIFICATION');
88
- }
89
- parseResult.sigAlg = decodeSigAlg;
90
- }
91
- /**
92
- * Validation part: validate the context of response after signature is verified and decrypted (optional)
93
- */
94
- const issuer = targetEntityMetadata.getEntityID();
95
- const extractedProperties = parseResult.extract;
96
- // unmatched issuer
97
- if ((parserType === 'LogoutResponse' || parserType === 'SAMLResponse')
98
- && extractedProperties
99
- && extractedProperties.issuer !== issuer) {
100
- return Promise.reject('ERR_UNMATCH_ISSUER');
101
- }
102
- // invalid session time
103
- // only run the verifyTime when `SessionNotOnOrAfter` exists
104
- if (parserType === 'SAMLResponse'
105
- && extractedProperties.sessionIndex.sessionNotOnOrAfter
106
- && !(0, validator_js_1.verifyTime)(undefined, extractedProperties.sessionIndex.sessionNotOnOrAfter, self.entitySetting.clockDrifts)) {
107
- return Promise.reject('ERR_EXPIRED_SESSION');
108
- }
109
- // invalid time
110
- // 2.4.1.2 https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
111
- if (parserType === 'SAMLResponse'
112
- && extractedProperties.conditions
113
- && !(0, validator_js_1.verifyTime)(extractedProperties.conditions.notBefore, extractedProperties.conditions.notOnOrAfter, self.entitySetting.clockDrifts)) {
114
- return Promise.reject('ERR_SUBJECT_UNCONFIRMED');
115
- }
116
- return Promise.resolve(parseResult);
117
- }
118
- // proceed the post flow
119
- async function postFlow(options) {
120
- const { request, from, self, parserType, checkSignature = true } = options;
121
- const { body } = request;
122
- const direction = libsaml_js_1.default.getQueryParamByType(parserType);
123
- const encodedRequest = body[direction];
124
- let samlContent = String((0, utility_js_1.base64Decode)(encodedRequest));
125
- const verificationOptions = {
126
- metadata: from.entityMeta,
127
- signatureAlgorithm: from.entitySetting.requestSignatureAlgorithm,
128
- };
129
- const decryptRequired = from.entitySetting.isAssertionEncrypted;
130
- let extractorFields = [];
131
- // validate the xml first
132
- await libsaml_js_1.default.isValidXml(samlContent);
133
- if (parserType !== urlParams.samlResponse) {
134
- extractorFields = getDefaultExtractorFields(parserType, null);
135
- }
136
- // check status based on different scenarios
137
- await checkStatus(samlContent, parserType);
138
- // verify the signatures (the response is encrypted then signed, then verify first then decrypt)
139
- if (checkSignature &&
140
- from.entitySetting.messageSigningOrder === urn_js_1.MessageSignatureOrder.ETS) {
141
- const [verified, verifiedAssertionNode] = libsaml_js_1.default.verifySignature(samlContent, verificationOptions);
142
- if (!verified) {
143
- return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
144
- }
145
- if (!decryptRequired) {
146
- extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
147
- }
148
- }
149
- if (parserType === 'SAMLResponse' && decryptRequired) {
150
- const result = await libsaml_js_1.default.decryptAssertion(self, samlContent);
151
- samlContent = result[0];
152
- extractorFields = getDefaultExtractorFields(parserType, result[1]);
153
- }
154
- // verify the signatures (the response is signed then encrypted, then decrypt first then verify)
155
- if (checkSignature &&
156
- from.entitySetting.messageSigningOrder === urn_js_1.MessageSignatureOrder.STE) {
157
- const [verified, verifiedAssertionNode] = libsaml_js_1.default.verifySignature(samlContent, verificationOptions);
158
- if (verified) {
159
- extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
160
- }
161
- else {
162
- return Promise.reject('ERR_FAIL_TO_VERIFY_STE_SIGNATURE');
163
- }
164
- }
165
- const parseResult = {
166
- samlContent: samlContent,
167
- extract: (0, extractor_js_1.extract)(samlContent, extractorFields),
168
- };
169
- /**
170
- * Validation part: validate the context of response after signature is verified and decrypted (optional)
171
- */
172
- const targetEntityMetadata = from.entityMeta;
173
- const issuer = targetEntityMetadata.getEntityID();
174
- const extractedProperties = parseResult.extract;
175
- // unmatched issuer
176
- if ((parserType === 'LogoutResponse' || parserType === 'SAMLResponse')
177
- && extractedProperties
178
- && extractedProperties.issuer !== issuer) {
179
- return Promise.reject('ERR_UNMATCH_ISSUER');
180
- }
181
- // invalid session time
182
- // only run the verifyTime when `SessionNotOnOrAfter` exists
183
- if (parserType === 'SAMLResponse'
184
- && extractedProperties.sessionIndex.sessionNotOnOrAfter
185
- && !(0, validator_js_1.verifyTime)(undefined, extractedProperties.sessionIndex.sessionNotOnOrAfter, self.entitySetting.clockDrifts)) {
186
- return Promise.reject('ERR_EXPIRED_SESSION');
187
- }
188
- // invalid time
189
- // 2.4.1.2 https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
190
- if (parserType === 'SAMLResponse'
191
- && extractedProperties.conditions
192
- && !(0, validator_js_1.verifyTime)(extractedProperties.conditions.notBefore, extractedProperties.conditions.notOnOrAfter, self.entitySetting.clockDrifts)) {
193
- return Promise.reject('ERR_SUBJECT_UNCONFIRMED');
194
- }
195
- return Promise.resolve(parseResult);
196
- }
197
- // proceed the post simple sign binding flow
198
- async function postSimpleSignFlow(options) {
199
- const { request, parserType, self, checkSignature = true, from } = options;
200
- const { body, octetString } = request;
201
- const targetEntityMetadata = from.entityMeta;
202
- // ?SAMLRequest= or ?SAMLResponse=
203
- const direction = libsaml_js_1.default.getQueryParamByType(parserType);
204
- const encodedRequest = body[direction];
205
- const sigAlg = body['SigAlg'];
206
- const signature = body['Signature'];
207
- // query must contain the saml content
208
- if (encodedRequest === undefined) {
209
- return Promise.reject('ERR_SIMPLESIGN_FLOW_BAD_ARGS');
210
- }
211
- const xmlString = String((0, utility_js_1.base64Decode)(encodedRequest));
212
- // validate the xml
213
- try {
214
- await libsaml_js_1.default.isValidXml(xmlString);
215
- }
216
- catch (e) {
217
- return Promise.reject('ERR_INVALID_XML');
218
- }
219
- // check status based on different scenarios
220
- await checkStatus(xmlString, parserType);
221
- let assertion = '';
222
- if (parserType === urlParams.samlResponse) {
223
- // Extract assertion shortcut
224
- const verifiedDoc = (0, extractor_js_1.extract)(xmlString, [{
225
- key: 'assertion',
226
- localPath: ['~Response', 'Assertion'],
227
- attributes: [],
228
- context: true
229
- }]);
230
- if (verifiedDoc && verifiedDoc.assertion) {
231
- assertion = verifiedDoc.assertion;
232
- }
233
- }
234
- const extractorFields = getDefaultExtractorFields(parserType, assertion.length > 0 ? assertion : null);
235
- const parseResult = {
236
- samlContent: xmlString,
237
- sigAlg: null,
238
- extract: (0, extractor_js_1.extract)(xmlString, extractorFields),
239
- };
240
- // see if signature check is required
241
- // only verify message signature is enough
242
- if (checkSignature) {
243
- if (!signature || !sigAlg) {
244
- return Promise.reject('ERR_MISSING_SIG_ALG');
245
- }
246
- // put the below two assignments into verifyMessageSignature function
247
- const base64Signature = Buffer.from(signature, 'base64');
248
- const verified = libsaml_js_1.default.verifyMessageSignature(targetEntityMetadata, octetString, base64Signature, sigAlg);
249
- if (!verified) {
250
- // Fail to verify message signature
251
- return Promise.reject('ERR_FAILED_MESSAGE_SIGNATURE_VERIFICATION');
252
- }
253
- parseResult.sigAlg = sigAlg;
254
- }
255
- /**
256
- * Validation part: validate the context of response after signature is verified and decrypted (optional)
257
- */
258
- const issuer = targetEntityMetadata.getEntityID();
259
- const extractedProperties = parseResult.extract;
260
- // unmatched issuer
261
- if ((parserType === 'LogoutResponse' || parserType === 'SAMLResponse')
262
- && extractedProperties
263
- && extractedProperties.issuer !== issuer) {
264
- return Promise.reject('ERR_UNMATCH_ISSUER');
265
- }
266
- // invalid session time
267
- // only run the verifyTime when `SessionNotOnOrAfter` exists
268
- if (parserType === 'SAMLResponse'
269
- && extractedProperties.sessionIndex.sessionNotOnOrAfter
270
- && !(0, validator_js_1.verifyTime)(undefined, extractedProperties.sessionIndex.sessionNotOnOrAfter, self.entitySetting.clockDrifts)) {
271
- return Promise.reject('ERR_EXPIRED_SESSION');
272
- }
273
- // invalid time
274
- // 2.4.1.2 https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
275
- if (parserType === 'SAMLResponse'
276
- && extractedProperties.conditions
277
- && !(0, validator_js_1.verifyTime)(extractedProperties.conditions.notBefore, extractedProperties.conditions.notOnOrAfter, self.entitySetting.clockDrifts)) {
278
- return Promise.reject('ERR_SUBJECT_UNCONFIRMED');
279
- }
280
- return Promise.resolve(parseResult);
281
- }
282
- function checkStatus(content, parserType) {
283
- // only check response parser
284
- if (parserType !== urlParams.samlResponse && parserType !== urlParams.logoutResponse) {
285
- return Promise.resolve('SKIPPED');
286
- }
287
- const fields = parserType === urlParams.samlResponse
288
- ? extractor_js_1.loginResponseStatusFields
289
- : extractor_js_1.logoutResponseStatusFields;
290
- const { top, second } = (0, extractor_js_1.extract)(content, fields);
291
- // only resolve when top-tier status code is success
292
- if (top === urn_js_1.StatusCode.Success) {
293
- return Promise.resolve('OK');
294
- }
295
- if (!top) {
296
- throw new Error('ERR_UNDEFINED_STATUS');
297
- }
298
- // returns a detailed error for two-tier error code
299
- throw new Error(`ERR_FAILED_STATUS with top tier code: ${top}, second tier code: ${second}`);
300
- }
301
- function flow(options) {
302
- const binding = options.binding;
303
- const parserType = options.parserType;
304
- options.supportBindings = [urn_js_1.BindingNamespace.Redirect, urn_js_1.BindingNamespace.Post, urn_js_1.BindingNamespace.SimpleSign];
305
- // saml response allows POST, REDIRECT
306
- if (parserType === urn_js_1.ParserType.SAMLResponse) {
307
- options.supportBindings = [urn_js_1.BindingNamespace.Post, urn_js_1.BindingNamespace.Redirect, urn_js_1.BindingNamespace.SimpleSign];
308
- }
309
- if (binding === bindDict.post) {
310
- return postFlow(options);
311
- }
312
- if (binding === bindDict.redirect) {
313
- return redirectFlow(options);
314
- }
315
- if (binding === bindDict.simpleSign) {
316
- return postSimpleSignFlow(options);
317
- }
318
- return Promise.reject('ERR_UNEXPECTED_FLOW');
319
- }
320
- exports.flow = flow;
1
+ import { inflateString, base64Decode } from './utility.js';
2
+ import { verifyTime } from './validator.js';
3
+ import libsaml from './libsaml.js';
4
+ import { extract, loginRequestFields, loginResponseFields, logoutRequestFields, logoutResponseFields, logoutResponseStatusFields, loginResponseStatusFields } from './extractor.js';
5
+ import { BindingNamespace, ParserType, wording, MessageSignatureOrder, StatusCode } from './urn.js';
6
+ const bindDict = wording.binding;
7
+ const urlParams = wording.urlParams;
8
+ // get the default extractor fields based on the parserType
9
+ function getDefaultExtractorFields(parserType, assertion) {
10
+ switch (parserType) {
11
+ case ParserType.SAMLRequest:
12
+ return loginRequestFields;
13
+ case ParserType.SAMLResponse:
14
+ if (!assertion) {
15
+ // unexpected hit
16
+ throw new Error('ERR_EMPTY_ASSERTION');
17
+ }
18
+ return loginResponseFields(assertion);
19
+ case ParserType.LogoutRequest:
20
+ return logoutRequestFields;
21
+ case ParserType.LogoutResponse:
22
+ return logoutResponseFields;
23
+ default:
24
+ throw new Error('ERR_UNDEFINED_PARSERTYPE');
25
+ }
26
+ }
27
+ // proceed the redirect binding flow
28
+ async function redirectFlow(options) {
29
+ const { request, parserType, self, checkSignature = true, from } = options;
30
+ const { query, octetString } = request;
31
+ const { SigAlg: sigAlg, Signature: signature } = query;
32
+ const targetEntityMetadata = from.entityMeta;
33
+ // ?SAMLRequest= or ?SAMLResponse=
34
+ const direction = libsaml.getQueryParamByType(parserType);
35
+ const content = query[direction];
36
+ // query must contain the saml content
37
+ if (content === undefined) {
38
+ return Promise.reject('ERR_REDIRECT_FLOW_BAD_ARGS');
39
+ }
40
+ const xmlString = inflateString(decodeURIComponent(content));
41
+ // validate the xml
42
+ try {
43
+ await libsaml.isValidXml(xmlString);
44
+ }
45
+ catch (e) {
46
+ return Promise.reject('ERR_INVALID_XML');
47
+ }
48
+ // check status based on different scenarios
49
+ await checkStatus(xmlString, parserType);
50
+ let assertion = '';
51
+ if (parserType === urlParams.samlResponse) {
52
+ // Extract assertion shortcut
53
+ const verifiedDoc = extract(xmlString, [{
54
+ key: 'assertion',
55
+ localPath: ['~Response', 'Assertion'],
56
+ attributes: [],
57
+ context: true
58
+ }]);
59
+ if (verifiedDoc && verifiedDoc.assertion) {
60
+ assertion = verifiedDoc.assertion;
61
+ }
62
+ }
63
+ const extractorFields = getDefaultExtractorFields(parserType, assertion.length > 0 ? assertion : null);
64
+ const parseResult = {
65
+ samlContent: xmlString,
66
+ sigAlg: null,
67
+ extract: extract(xmlString, extractorFields),
68
+ };
69
+ // see if signature check is required
70
+ // only verify message signature is enough
71
+ if (checkSignature) {
72
+ if (!signature || !sigAlg) {
73
+ return Promise.reject('ERR_MISSING_SIG_ALG');
74
+ }
75
+ // put the below two assignments into verifyMessageSignature function
76
+ const base64Signature = Buffer.from(decodeURIComponent(signature), 'base64');
77
+ const decodeSigAlg = decodeURIComponent(sigAlg);
78
+ const verified = libsaml.verifyMessageSignature(targetEntityMetadata, octetString, base64Signature, sigAlg);
79
+ if (!verified) {
80
+ // Fail to verify message signature
81
+ return Promise.reject('ERR_FAILED_MESSAGE_SIGNATURE_VERIFICATION');
82
+ }
83
+ parseResult.sigAlg = decodeSigAlg;
84
+ }
85
+ /**
86
+ * Validation part: validate the context of response after signature is verified and decrypted (optional)
87
+ */
88
+ const issuer = targetEntityMetadata.getEntityID();
89
+ const extractedProperties = parseResult.extract;
90
+ // unmatched issuer
91
+ if ((parserType === 'LogoutResponse' || parserType === 'SAMLResponse')
92
+ && extractedProperties
93
+ && extractedProperties.issuer !== issuer) {
94
+ return Promise.reject('ERR_UNMATCH_ISSUER');
95
+ }
96
+ // invalid session time
97
+ // only run the verifyTime when `SessionNotOnOrAfter` exists
98
+ if (parserType === 'SAMLResponse'
99
+ && extractedProperties.sessionIndex.sessionNotOnOrAfter
100
+ && !verifyTime(undefined, extractedProperties.sessionIndex.sessionNotOnOrAfter, self.entitySetting.clockDrifts)) {
101
+ return Promise.reject('ERR_EXPIRED_SESSION');
102
+ }
103
+ // invalid time
104
+ // 2.4.1.2 https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
105
+ if (parserType === 'SAMLResponse'
106
+ && extractedProperties.conditions
107
+ && !verifyTime(extractedProperties.conditions.notBefore, extractedProperties.conditions.notOnOrAfter, self.entitySetting.clockDrifts)) {
108
+ return Promise.reject('ERR_SUBJECT_UNCONFIRMED');
109
+ }
110
+ return Promise.resolve(parseResult);
111
+ }
112
+ // proceed the post flow
113
+ async function postFlow(options) {
114
+ const { request, from, self, parserType, checkSignature = true } = options;
115
+ const { body } = request;
116
+ const direction = libsaml.getQueryParamByType(parserType);
117
+ const encodedRequest = body[direction];
118
+ let samlContent = String(base64Decode(encodedRequest));
119
+ const verificationOptions = {
120
+ metadata: from.entityMeta,
121
+ signatureAlgorithm: from.entitySetting.requestSignatureAlgorithm,
122
+ };
123
+ const decryptRequired = from.entitySetting.isAssertionEncrypted;
124
+ let extractorFields = [];
125
+ // validate the xml first
126
+ await libsaml.isValidXml(samlContent);
127
+ if (parserType !== urlParams.samlResponse) {
128
+ extractorFields = getDefaultExtractorFields(parserType, null);
129
+ }
130
+ // check status based on different scenarios
131
+ await checkStatus(samlContent, parserType);
132
+ // verify the signatures (the response is encrypted then signed, then verify first then decrypt)
133
+ if (checkSignature &&
134
+ from.entitySetting.messageSigningOrder === MessageSignatureOrder.ETS) {
135
+ const [verified, verifiedAssertionNode] = libsaml.verifySignature(samlContent, verificationOptions);
136
+ if (!verified) {
137
+ return Promise.reject('ERR_FAIL_TO_VERIFY_ETS_SIGNATURE');
138
+ }
139
+ if (!decryptRequired) {
140
+ extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
141
+ }
142
+ }
143
+ if (parserType === 'SAMLResponse' && decryptRequired) {
144
+ const result = await libsaml.decryptAssertion(self, samlContent);
145
+ samlContent = result[0];
146
+ extractorFields = getDefaultExtractorFields(parserType, result[1]);
147
+ }
148
+ // verify the signatures (the response is signed then encrypted, then decrypt first then verify)
149
+ if (checkSignature &&
150
+ from.entitySetting.messageSigningOrder === MessageSignatureOrder.STE) {
151
+ const [verified, verifiedAssertionNode] = libsaml.verifySignature(samlContent, verificationOptions);
152
+ if (verified) {
153
+ extractorFields = getDefaultExtractorFields(parserType, verifiedAssertionNode);
154
+ }
155
+ else {
156
+ return Promise.reject('ERR_FAIL_TO_VERIFY_STE_SIGNATURE');
157
+ }
158
+ }
159
+ const parseResult = {
160
+ samlContent: samlContent,
161
+ extract: extract(samlContent, extractorFields),
162
+ };
163
+ /**
164
+ * Validation part: validate the context of response after signature is verified and decrypted (optional)
165
+ */
166
+ const targetEntityMetadata = from.entityMeta;
167
+ const issuer = targetEntityMetadata.getEntityID();
168
+ const extractedProperties = parseResult.extract;
169
+ // unmatched issuer
170
+ if ((parserType === 'LogoutResponse' || parserType === 'SAMLResponse')
171
+ && extractedProperties
172
+ && extractedProperties.issuer !== issuer) {
173
+ return Promise.reject('ERR_UNMATCH_ISSUER');
174
+ }
175
+ // invalid session time
176
+ // only run the verifyTime when `SessionNotOnOrAfter` exists
177
+ if (parserType === 'SAMLResponse'
178
+ && extractedProperties.sessionIndex.sessionNotOnOrAfter
179
+ && !verifyTime(undefined, extractedProperties.sessionIndex.sessionNotOnOrAfter, self.entitySetting.clockDrifts)) {
180
+ return Promise.reject('ERR_EXPIRED_SESSION');
181
+ }
182
+ // invalid time
183
+ // 2.4.1.2 https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
184
+ if (parserType === 'SAMLResponse'
185
+ && extractedProperties.conditions
186
+ && !verifyTime(extractedProperties.conditions.notBefore, extractedProperties.conditions.notOnOrAfter, self.entitySetting.clockDrifts)) {
187
+ return Promise.reject('ERR_SUBJECT_UNCONFIRMED');
188
+ }
189
+ return Promise.resolve(parseResult);
190
+ }
191
+ // proceed the post simple sign binding flow
192
+ async function postSimpleSignFlow(options) {
193
+ const { request, parserType, self, checkSignature = true, from } = options;
194
+ const { body, octetString } = request;
195
+ const targetEntityMetadata = from.entityMeta;
196
+ // ?SAMLRequest= or ?SAMLResponse=
197
+ const direction = libsaml.getQueryParamByType(parserType);
198
+ const encodedRequest = body[direction];
199
+ const sigAlg = body['SigAlg'];
200
+ const signature = body['Signature'];
201
+ // query must contain the saml content
202
+ if (encodedRequest === undefined) {
203
+ return Promise.reject('ERR_SIMPLESIGN_FLOW_BAD_ARGS');
204
+ }
205
+ const xmlString = String(base64Decode(encodedRequest));
206
+ // validate the xml
207
+ try {
208
+ await libsaml.isValidXml(xmlString);
209
+ }
210
+ catch (e) {
211
+ return Promise.reject('ERR_INVALID_XML');
212
+ }
213
+ // check status based on different scenarios
214
+ await checkStatus(xmlString, parserType);
215
+ let assertion = '';
216
+ if (parserType === urlParams.samlResponse) {
217
+ // Extract assertion shortcut
218
+ const verifiedDoc = extract(xmlString, [{
219
+ key: 'assertion',
220
+ localPath: ['~Response', 'Assertion'],
221
+ attributes: [],
222
+ context: true
223
+ }]);
224
+ if (verifiedDoc && verifiedDoc.assertion) {
225
+ assertion = verifiedDoc.assertion;
226
+ }
227
+ }
228
+ const extractorFields = getDefaultExtractorFields(parserType, assertion.length > 0 ? assertion : null);
229
+ const parseResult = {
230
+ samlContent: xmlString,
231
+ sigAlg: null,
232
+ extract: extract(xmlString, extractorFields),
233
+ };
234
+ // see if signature check is required
235
+ // only verify message signature is enough
236
+ if (checkSignature) {
237
+ if (!signature || !sigAlg) {
238
+ return Promise.reject('ERR_MISSING_SIG_ALG');
239
+ }
240
+ // put the below two assignments into verifyMessageSignature function
241
+ const base64Signature = Buffer.from(signature, 'base64');
242
+ const verified = libsaml.verifyMessageSignature(targetEntityMetadata, octetString, base64Signature, sigAlg);
243
+ if (!verified) {
244
+ // Fail to verify message signature
245
+ return Promise.reject('ERR_FAILED_MESSAGE_SIGNATURE_VERIFICATION');
246
+ }
247
+ parseResult.sigAlg = sigAlg;
248
+ }
249
+ /**
250
+ * Validation part: validate the context of response after signature is verified and decrypted (optional)
251
+ */
252
+ const issuer = targetEntityMetadata.getEntityID();
253
+ const extractedProperties = parseResult.extract;
254
+ // unmatched issuer
255
+ if ((parserType === 'LogoutResponse' || parserType === 'SAMLResponse')
256
+ && extractedProperties
257
+ && extractedProperties.issuer !== issuer) {
258
+ return Promise.reject('ERR_UNMATCH_ISSUER');
259
+ }
260
+ // invalid session time
261
+ // only run the verifyTime when `SessionNotOnOrAfter` exists
262
+ if (parserType === 'SAMLResponse'
263
+ && extractedProperties.sessionIndex.sessionNotOnOrAfter
264
+ && !verifyTime(undefined, extractedProperties.sessionIndex.sessionNotOnOrAfter, self.entitySetting.clockDrifts)) {
265
+ return Promise.reject('ERR_EXPIRED_SESSION');
266
+ }
267
+ // invalid time
268
+ // 2.4.1.2 https://docs.oasis-open.org/security/saml/v2.0/saml-core-2.0-os.pdf
269
+ if (parserType === 'SAMLResponse'
270
+ && extractedProperties.conditions
271
+ && !verifyTime(extractedProperties.conditions.notBefore, extractedProperties.conditions.notOnOrAfter, self.entitySetting.clockDrifts)) {
272
+ return Promise.reject('ERR_SUBJECT_UNCONFIRMED');
273
+ }
274
+ return Promise.resolve(parseResult);
275
+ }
276
+ function checkStatus(content, parserType) {
277
+ // only check response parser
278
+ if (parserType !== urlParams.samlResponse && parserType !== urlParams.logoutResponse) {
279
+ return Promise.resolve('SKIPPED');
280
+ }
281
+ const fields = parserType === urlParams.samlResponse
282
+ ? loginResponseStatusFields
283
+ : logoutResponseStatusFields;
284
+ const { top, second } = extract(content, fields);
285
+ // only resolve when top-tier status code is success
286
+ if (top === StatusCode.Success) {
287
+ return Promise.resolve('OK');
288
+ }
289
+ if (!top) {
290
+ throw new Error('ERR_UNDEFINED_STATUS');
291
+ }
292
+ // returns a detailed error for two-tier error code
293
+ throw new Error(`ERR_FAILED_STATUS with top tier code: ${top}, second tier code: ${second}`);
294
+ }
295
+ export function flow(options) {
296
+ const binding = options.binding;
297
+ const parserType = options.parserType;
298
+ options.supportBindings = [BindingNamespace.Redirect, BindingNamespace.Post, BindingNamespace.SimpleSign];
299
+ // saml response allows POST, REDIRECT
300
+ if (parserType === ParserType.SAMLResponse) {
301
+ options.supportBindings = [BindingNamespace.Post, BindingNamespace.Redirect, BindingNamespace.SimpleSign];
302
+ }
303
+ if (binding === bindDict.post) {
304
+ return postFlow(options);
305
+ }
306
+ if (binding === bindDict.redirect) {
307
+ return redirectFlow(options);
308
+ }
309
+ if (binding === bindDict.simpleSign) {
310
+ return postSimpleSignFlow(options);
311
+ }
312
+ return Promise.reject('ERR_UNEXPECTED_FLOW');
313
+ }
321
314
  //# sourceMappingURL=flow.js.map