samlify 2.12.0 → 2.13.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 +1 -1
- package/build/src/api.js +41 -3
- package/build/src/api.js.map +1 -1
- package/build/src/binding-post.js +236 -182
- package/build/src/binding-post.js.map +1 -1
- package/build/src/binding-redirect.js +303 -215
- package/build/src/binding-redirect.js.map +1 -1
- package/build/src/binding-simplesign.js +285 -137
- package/build/src/binding-simplesign.js.map +1 -1
- package/build/src/entity-idp.js +130 -47
- package/build/src/entity-idp.js.map +1 -1
- package/build/src/entity-sp.js +81 -39
- package/build/src/entity-sp.js.map +1 -1
- package/build/src/entity.js +100 -62
- package/build/src/entity.js.map +1 -1
- package/build/src/extractor.js +118 -151
- package/build/src/extractor.js.map +1 -1
- package/build/src/flow.js +100 -96
- package/build/src/flow.js.map +1 -1
- package/build/src/libsaml.js +315 -259
- package/build/src/libsaml.js.map +1 -1
- package/build/src/metadata-idp.js +60 -30
- package/build/src/metadata-idp.js.map +1 -1
- package/build/src/metadata-sp.js +51 -41
- package/build/src/metadata-sp.js.map +1 -1
- package/build/src/metadata.js +47 -43
- package/build/src/metadata.js.map +1 -1
- package/build/src/options.js +73 -0
- package/build/src/options.js.map +1 -0
- package/build/src/urn.js +28 -1
- package/build/src/urn.js.map +1 -1
- package/build/src/utility.js +140 -85
- package/build/src/utility.js.map +1 -1
- package/build/src/validator.js +27 -10
- package/build/src/validator.js.map +1 -1
- package/package.json +16 -5
- package/types/src/api.d.ts +33 -3
- package/types/src/binding-post.d.ts +67 -34
- package/types/src/binding-redirect.d.ts +58 -31
- package/types/src/binding-simplesign.d.ts +77 -21
- package/types/src/entity-idp.d.ts +40 -31
- package/types/src/entity-sp.d.ts +37 -27
- package/types/src/entity.d.ts +71 -77
- package/types/src/extractor.d.ts +31 -22
- package/types/src/flow.d.ts +24 -2
- package/types/src/libsaml.d.ts +172 -118
- package/types/src/metadata-idp.d.ts +27 -11
- package/types/src/metadata-sp.d.ts +29 -19
- package/types/src/metadata.d.ts +59 -34
- package/types/src/options.d.ts +37 -0
- package/types/src/types.d.ts +250 -24
- package/types/src/urn.d.ts +7 -0
- package/types/src/utility.d.ts +139 -90
- package/types/src/validator.d.ts +21 -0
- package/.circleci/config.yml +0 -98
- package/.editorconfig +0 -19
- package/.github/FUNDING.yml +0 -1
- package/.github/workflows/deploy-docs.yml +0 -56
- package/.pre-commit.sh +0 -15
- package/.snyk +0 -4
- package/Makefile +0 -25
- package/index.ts +0 -28
- package/samlify-2.11.0.tgz +0 -0
- package/src/api.ts +0 -48
- package/src/binding-post.ts +0 -336
- package/src/binding-redirect.ts +0 -335
- package/src/binding-simplesign.ts +0 -231
- package/src/entity-idp.ts +0 -145
- package/src/entity-sp.ts +0 -114
- package/src/entity.ts +0 -243
- package/src/extractor.ts +0 -399
- package/src/flow.ts +0 -469
- package/src/libsaml.ts +0 -779
- package/src/metadata-idp.ts +0 -146
- package/src/metadata-sp.ts +0 -203
- package/src/metadata.ts +0 -166
- package/src/types.ts +0 -127
- package/src/urn.ts +0 -210
- package/src/utility.ts +0 -259
- package/src/validator.ts +0 -44
- package/tsconfig.json +0 -41
- package/tslint.json +0 -35
- package/types.d.ts +0 -2
- package/vitest.config.ts +0 -12
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
[](https://app.circleci.com/pipelines/github/tngan/samlify)
|
|
4
4
|
[](https://www.npmjs.com/package/samlify)
|
|
5
5
|
[](https://www.npmjs.com/package/samlify)
|
|
6
|
-
[](./vitest.config.ts)
|
|
7
7
|
|
|
8
8
|
Highly configuarable Node.js SAML 2.0 library for Single Sign On
|
|
9
9
|
|
package/build/src/api.js
CHANGED
|
@@ -1,8 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __assign = (this && this.__assign) || function () {
|
|
3
|
+
__assign = Object.assign || function(t) {
|
|
4
|
+
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
5
|
+
s = arguments[i];
|
|
6
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
|
|
7
|
+
t[p] = s[p];
|
|
8
|
+
}
|
|
9
|
+
return t;
|
|
10
|
+
};
|
|
11
|
+
return __assign.apply(this, arguments);
|
|
12
|
+
};
|
|
2
13
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
14
|
exports.getContext = getContext;
|
|
4
15
|
exports.setSchemaValidator = setSchemaValidator;
|
|
5
16
|
exports.setDOMParserOptions = setDOMParserOptions;
|
|
17
|
+
/**
|
|
18
|
+
* @file api.ts
|
|
19
|
+
* @author tngan
|
|
20
|
+
* @desc Global module configuration: XML schema validator and DOM parser.
|
|
21
|
+
*/
|
|
6
22
|
var xmldom_1 = require("@xmldom/xmldom");
|
|
7
23
|
var XXE_SAFE_OPTIONS = {
|
|
8
24
|
/**
|
|
@@ -17,20 +33,42 @@ var XXE_SAFE_OPTIONS = {
|
|
|
17
33
|
};
|
|
18
34
|
var context = {
|
|
19
35
|
validate: undefined,
|
|
20
|
-
dom: new xmldom_1.DOMParser(XXE_SAFE_OPTIONS)
|
|
36
|
+
dom: new xmldom_1.DOMParser(XXE_SAFE_OPTIONS),
|
|
21
37
|
};
|
|
38
|
+
/**
|
|
39
|
+
* Return the module-wide runtime context (DOM parser and validator).
|
|
40
|
+
*
|
|
41
|
+
* @returns shared context object
|
|
42
|
+
*/
|
|
22
43
|
function getContext() {
|
|
23
44
|
return context;
|
|
24
45
|
}
|
|
46
|
+
/**
|
|
47
|
+
* Register the caller-supplied SAML schema validator. Throws when the
|
|
48
|
+
* supplied value does not expose a `validate` callback.
|
|
49
|
+
*
|
|
50
|
+
* @param params object with a `validate(xml)` callback
|
|
51
|
+
*/
|
|
25
52
|
function setSchemaValidator(params) {
|
|
26
53
|
if (typeof params.validate !== 'function') {
|
|
27
54
|
throw new Error('validate must be a callback function having one argument as xml input');
|
|
28
55
|
}
|
|
29
|
-
// assign the validate function to the context
|
|
30
56
|
context.validate = params.validate;
|
|
31
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Replace the module-wide DOM parser with one configured by the caller.
|
|
60
|
+
*
|
|
61
|
+
* The XXE-safe error handlers are merged into the supplied options as a
|
|
62
|
+
* baseline so callers can override unrelated settings without
|
|
63
|
+
* accidentally disabling XXE protection (`saml-core §6.4`,
|
|
64
|
+
* `saml-sec-consider §6.3.1`). A caller can still opt out by passing
|
|
65
|
+
* its own `errorHandler`, but it must do so explicitly.
|
|
66
|
+
*
|
|
67
|
+
* @param options xmldom parser options
|
|
68
|
+
*/
|
|
32
69
|
function setDOMParserOptions(options) {
|
|
70
|
+
var _a;
|
|
33
71
|
if (options === void 0) { options = {}; }
|
|
34
|
-
context.dom = new xmldom_1.DOMParser(options);
|
|
72
|
+
context.dom = new xmldom_1.DOMParser(__assign(__assign(__assign({}, XXE_SAFE_OPTIONS), options), { errorHandler: (_a = options.errorHandler) !== null && _a !== void 0 ? _a : XXE_SAFE_OPTIONS.errorHandler }));
|
|
35
73
|
}
|
|
36
74
|
//# sourceMappingURL=api.js.map
|
package/build/src/api.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"api.js","sourceRoot":"","sources":["../../src/api.ts"],"names":[],"mappings":";;;;;;;;;;;;;AA0CA,gCAEC;AAQD,gDAKC;AAaD,kDAMC;AA5ED;;;;GAIG;AACH,yCAA+E;AAe/E,IAAM,gBAAgB,GAAqB;IACzC;;;;OAIG;IACH,YAAY,EAAE;QACZ,KAAK,EAAE,UAAC,GAAW,IAAO,MAAM,IAAI,KAAK,CAAC,6BAAsB,GAAG,CAAE,CAAC,CAAC,CAAC,CAAC;QACzE,UAAU,EAAE,UAAC,GAAW,IAAO,MAAM,IAAI,KAAK,CAAC,2BAAoB,GAAG,CAAE,CAAC,CAAC,CAAC,CAAC;KAC7E;CACF,CAAC;AAEF,IAAM,OAAO,GAAY;IACvB,QAAQ,EAAE,SAAS;IACnB,GAAG,EAAE,IAAI,kBAAG,CAAC,gBAAgB,CAAC;CAC/B,CAAC;AAEF;;;;GAIG;AACH,SAAgB,UAAU;IACxB,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;GAKG;AACH,SAAgB,kBAAkB,CAAC,MAAwB;IACzD,IAAI,OAAO,MAAM,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;QAC1C,MAAM,IAAI,KAAK,CAAC,uEAAuE,CAAC,CAAC;IAC3F,CAAC;IACD,OAAO,CAAC,QAAQ,GAAG,MAAM,CAAC,QAAQ,CAAC;AACrC,CAAC;AAED;;;;;;;;;;GAUG;AACH,SAAgB,mBAAmB,CAAC,OAA8B;;IAA9B,wBAAA,EAAA,YAA8B;IAChE,OAAO,CAAC,GAAG,GAAG,IAAI,kBAAG,gCAChB,gBAAgB,GAChB,OAAO,KACV,YAAY,EAAE,MAAA,OAAO,CAAC,YAAY,mCAAI,gBAAgB,CAAC,YAAY,IACnE,CAAC;AACL,CAAC"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
/**
|
|
3
|
-
* @file binding-post.ts
|
|
4
|
-
* @author tngan
|
|
5
|
-
* @desc Binding-level API
|
|
6
|
-
|
|
3
|
+
* @file binding-post.ts
|
|
4
|
+
* @author tngan
|
|
5
|
+
* @desc Binding-level API for SAML HTTP-POST. Builds base64 login/logout
|
|
6
|
+
* request and response payloads that callers embed in an auto-submitting
|
|
7
|
+
* HTML form.
|
|
8
|
+
*/
|
|
7
9
|
var __assign = (this && this.__assign) || function () {
|
|
8
10
|
__assign = Object.assign || function(t) {
|
|
9
11
|
for (var s, i = 1, n = arguments.length; i < n; i++) {
|
|
@@ -93,81 +95,115 @@ var libsaml_1 = __importDefault(require("./libsaml"));
|
|
|
93
95
|
var utility_1 = __importStar(require("./utility"));
|
|
94
96
|
var binding = urn_1.wording.binding;
|
|
95
97
|
/**
|
|
96
|
-
*
|
|
97
|
-
*
|
|
98
|
-
* @param
|
|
99
|
-
* @param
|
|
100
|
-
|
|
101
|
-
|
|
98
|
+
* Generate a base64-encoded AuthnRequest for the HTTP-POST binding.
|
|
99
|
+
*
|
|
100
|
+
* @param referenceTagXPath XPath used when signing the request
|
|
101
|
+
* @param entity `{ idp, sp }` handles
|
|
102
|
+
* @param customTagReplacement optional custom template transformer
|
|
103
|
+
* @param forceAuthn per-request `ForceAuthn` flag (saml-core §3.4.1)
|
|
104
|
+
* @param assertionConsumerServiceIndex per-request ACS index (saml-core §3.4.1).
|
|
105
|
+
* Mutually exclusive with `AssertionConsumerServiceURL` / `ProtocolBinding`;
|
|
106
|
+
* when supplied, both of those attributes are dropped from the rendered XML.
|
|
107
|
+
* @returns id / base64-XML pair
|
|
108
|
+
*/
|
|
109
|
+
function base64LoginRequest(referenceTagXPath, entity, customTagReplacement, forceAuthn, assertionConsumerServiceIndex) {
|
|
110
|
+
var _a, _b;
|
|
102
111
|
var metadata = { idp: entity.idp.entityMeta, sp: entity.sp.entityMeta };
|
|
103
112
|
var spSetting = entity.sp.entitySetting;
|
|
104
113
|
var id = '';
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
114
|
+
/* v8 ignore start */
|
|
115
|
+
if (!metadata.idp || !metadata.sp) {
|
|
116
|
+
throw new Error('ERR_GENERATE_POST_LOGIN_REQUEST_MISSING_METADATA');
|
|
117
|
+
}
|
|
118
|
+
/* v8 ignore stop */
|
|
119
|
+
var base = metadata.idp.getSingleSignOnService(binding.post);
|
|
120
|
+
var rawSamlRequest;
|
|
121
|
+
if (customTagReplacement) {
|
|
122
|
+
// saml-bindings §3.5 — the AuthnRequest template is informative, not
|
|
123
|
+
// normative. Honour the callback regardless of whether the caller
|
|
124
|
+
// supplied a custom template (closes #549). Pass the user-supplied
|
|
125
|
+
// template when present; otherwise the library default.
|
|
126
|
+
var templateContext = (_b = (_a = spSetting.loginRequestTemplate) === null || _a === void 0 ? void 0 : _a.context) !== null && _b !== void 0 ? _b : libsaml_1.default.defaultLoginRequestTemplate.context;
|
|
127
|
+
var info = customTagReplacement(templateContext);
|
|
128
|
+
id = (0, utility_1.get)(info, 'id');
|
|
129
|
+
rawSamlRequest = (0, utility_1.get)(info, 'context');
|
|
130
|
+
}
|
|
131
|
+
else {
|
|
132
|
+
var nameIDFormat = spSetting.nameIDFormat;
|
|
133
|
+
var selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
|
|
134
|
+
id = spSetting.generateID();
|
|
135
|
+
// saml-core §3.4.1 — `AssertionConsumerServiceIndex` is mutually
|
|
136
|
+
// exclusive with `AssertionConsumerServiceURL` / `ProtocolBinding`.
|
|
137
|
+
// When the caller supplies the index we set the URL+ProtocolBinding
|
|
138
|
+
// tags to undefined so `replaceTagsByValue` drops both attributes
|
|
139
|
+
// from the rendered XML (closes #437).
|
|
140
|
+
var useAcsIndex = assertionConsumerServiceIndex !== undefined;
|
|
141
|
+
var tags = {
|
|
142
|
+
ID: id,
|
|
143
|
+
Destination: base,
|
|
144
|
+
Issuer: metadata.sp.getEntityID(),
|
|
145
|
+
IssueInstant: new Date().toISOString(),
|
|
146
|
+
ProtocolBinding: useAcsIndex
|
|
147
|
+
? undefined
|
|
148
|
+
: 'urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST',
|
|
149
|
+
AssertionConsumerServiceURL: useAcsIndex
|
|
150
|
+
? undefined
|
|
151
|
+
: metadata.sp.getAssertionConsumerService(binding.post),
|
|
152
|
+
AssertionConsumerServiceIndex: assertionConsumerServiceIndex,
|
|
153
|
+
EntityID: metadata.sp.getEntityID(),
|
|
154
|
+
AllowCreate: spSetting.allowCreate,
|
|
155
|
+
NameIDFormat: selectedNameIDFormat,
|
|
156
|
+
// saml-core §3.4.1 — `replaceTagsByValue` drops the attribute when
|
|
157
|
+
// `forceAuthn` is undefined, matching `use="optional"`.
|
|
158
|
+
ForceAuthn: forceAuthn,
|
|
159
|
+
};
|
|
160
|
+
rawSamlRequest = libsaml_1.default.replaceTagsByValue(libsaml_1.default.defaultLoginRequestTemplate.context, tags);
|
|
161
|
+
}
|
|
162
|
+
if (metadata.idp.isWantAuthnRequestsSigned()) {
|
|
163
|
+
var privateKey = spSetting.privateKey, privateKeyPass = spSetting.privateKeyPass, signatureAlgorithm = spSetting.requestSignatureAlgorithm, transformationAlgorithms = spSetting.transformationAlgorithms;
|
|
148
164
|
return {
|
|
149
165
|
id: id,
|
|
150
|
-
context:
|
|
166
|
+
context: libsaml_1.default.constructSAMLSignature({
|
|
167
|
+
referenceTagXPath: referenceTagXPath,
|
|
168
|
+
privateKey: privateKey,
|
|
169
|
+
privateKeyPass: privateKeyPass,
|
|
170
|
+
signatureAlgorithm: signatureAlgorithm,
|
|
171
|
+
transformationAlgorithms: transformationAlgorithms,
|
|
172
|
+
rawSamlMessage: rawSamlRequest,
|
|
173
|
+
signingCert: metadata.sp.getX509Certificate('signing'),
|
|
174
|
+
signatureConfig: spSetting.signatureConfig || {
|
|
175
|
+
prefix: 'ds',
|
|
176
|
+
location: { reference: "/*[local-name(.)='AuthnRequest']/*[local-name(.)='Issuer']", action: 'after' },
|
|
177
|
+
},
|
|
178
|
+
}),
|
|
151
179
|
};
|
|
152
180
|
}
|
|
153
|
-
|
|
181
|
+
return {
|
|
182
|
+
id: id,
|
|
183
|
+
context: utility_1.default.base64Encode(rawSamlRequest),
|
|
184
|
+
};
|
|
154
185
|
}
|
|
155
186
|
/**
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
159
|
-
*
|
|
160
|
-
* @param
|
|
161
|
-
* @param
|
|
162
|
-
|
|
187
|
+
* Generate a base64-encoded login response for the HTTP-POST binding.
|
|
188
|
+
* Supports the sign-then-encrypt and encrypt-then-sign pipelines based on
|
|
189
|
+
* `encryptThenSign`.
|
|
190
|
+
*
|
|
191
|
+
* @param requestInfo parsed login request used to link `InResponseTo`
|
|
192
|
+
* @param entity `{ idp, sp }` handles
|
|
193
|
+
* @param user authenticated user
|
|
194
|
+
* @param customTagReplacement optional custom template transformer
|
|
195
|
+
* @param encryptThenSign when true, encrypt the assertion first then sign
|
|
196
|
+
* @returns id / base64-XML pair
|
|
197
|
+
*/
|
|
163
198
|
function base64LoginResponse() {
|
|
164
199
|
return __awaiter(this, arguments, void 0, function (requestInfo, entity, user, customTagReplacement, encryptThenSign) {
|
|
165
|
-
var idpSetting, spSetting, id, metadata, nameIDFormat, selectedNameIDFormat, base, rawSamlResponse, nowTime, spEntityID, fiveMinutesLaterTime, fiveMinutesLater, now, acl, tvalue, template, privateKey, privateKeyPass, signatureAlgorithm, config, context;
|
|
200
|
+
var idpSetting, spSetting, id, metadata, nameIDFormat, selectedNameIDFormat, base, rawSamlResponse, nowTime, spEntityID, fiveMinutesLaterTime, fiveMinutesLater, now, acl, tvalue, templateContext, template, baseTemplate, privateKey, privateKeyPass, signatureAlgorithm, config, context;
|
|
201
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j;
|
|
166
202
|
if (requestInfo === void 0) { requestInfo = {}; }
|
|
167
203
|
if (user === void 0) { user = {}; }
|
|
168
204
|
if (encryptThenSign === void 0) { encryptThenSign = false; }
|
|
169
|
-
return __generator(this, function (
|
|
170
|
-
switch (
|
|
205
|
+
return __generator(this, function (_k) {
|
|
206
|
+
switch (_k.label) {
|
|
171
207
|
case 0:
|
|
172
208
|
idpSetting = entity.idp.entitySetting;
|
|
173
209
|
spSetting = entity.sp.entitySetting;
|
|
@@ -178,9 +214,11 @@ function base64LoginResponse() {
|
|
|
178
214
|
};
|
|
179
215
|
nameIDFormat = idpSetting.nameIDFormat;
|
|
180
216
|
selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
|
|
181
|
-
|
|
217
|
+
/* v8 ignore start */
|
|
218
|
+
if (!metadata.idp || !metadata.sp) {
|
|
219
|
+
throw new Error('ERR_GENERATE_POST_LOGIN_RESPONSE_MISSING_METADATA');
|
|
220
|
+
}
|
|
182
221
|
base = metadata.sp.getAssertionConsumerService(binding.post);
|
|
183
|
-
rawSamlResponse = void 0;
|
|
184
222
|
nowTime = new Date();
|
|
185
223
|
spEntityID = metadata.sp.getEntityID();
|
|
186
224
|
fiveMinutesLaterTime = new Date(nowTime.getTime());
|
|
@@ -199,7 +237,6 @@ function base64LoginResponse() {
|
|
|
199
237
|
IssueInstant: now,
|
|
200
238
|
AssertionConsumerServiceURL: acl,
|
|
201
239
|
StatusCode: urn_1.StatusCode.Success,
|
|
202
|
-
// can be customized
|
|
203
240
|
ConditionsNotBefore: now,
|
|
204
241
|
ConditionsNotOnOrAfter: fiveMinutesLater,
|
|
205
242
|
SubjectConfirmationDataNotOnOrAfter: fiveMinutesLater,
|
|
@@ -209,15 +246,17 @@ function base64LoginResponse() {
|
|
|
209
246
|
AuthnStatement: '',
|
|
210
247
|
AttributeStatement: '',
|
|
211
248
|
};
|
|
212
|
-
if (
|
|
213
|
-
|
|
214
|
-
|
|
249
|
+
if (customTagReplacement) {
|
|
250
|
+
templateContext = (_e = (_b = (_a = idpSetting.loginResponseTemplate) === null || _a === void 0 ? void 0 : _a.context) !== null && _b !== void 0 ? _b : (_d = (_c = idpSetting.tagPrefixedDefaults) === null || _c === void 0 ? void 0 : _c.loginResponseTemplate) === null || _d === void 0 ? void 0 : _d.context) !== null && _e !== void 0 ? _e : libsaml_1.default.defaultLoginResponseTemplate.context;
|
|
251
|
+
template = customTagReplacement(templateContext);
|
|
252
|
+
rawSamlResponse = (0, utility_1.get)(template, 'context');
|
|
215
253
|
}
|
|
216
254
|
else {
|
|
217
|
-
if (requestInfo !== null) {
|
|
255
|
+
if (requestInfo !== null && ((_f = requestInfo.extract) === null || _f === void 0 ? void 0 : _f.request)) {
|
|
218
256
|
tvalue.InResponseTo = requestInfo.extract.request.id;
|
|
219
257
|
}
|
|
220
|
-
|
|
258
|
+
baseTemplate = (_j = (_h = (_g = idpSetting.tagPrefixedDefaults) === null || _g === void 0 ? void 0 : _g.loginResponseTemplate) === null || _h === void 0 ? void 0 : _h.context) !== null && _j !== void 0 ? _j : libsaml_1.default.defaultLoginResponseTemplate.context;
|
|
259
|
+
rawSamlResponse = libsaml_1.default.replaceTagsByValue(baseTemplate, tvalue);
|
|
221
260
|
}
|
|
222
261
|
privateKey = idpSetting.privateKey, privateKeyPass = idpSetting.privateKeyPass, signatureAlgorithm = idpSetting.requestSignatureAlgorithm;
|
|
223
262
|
config = {
|
|
@@ -227,18 +266,14 @@ function base64LoginResponse() {
|
|
|
227
266
|
signingCert: metadata.idp.getX509Certificate('signing'),
|
|
228
267
|
isBase64Output: false,
|
|
229
268
|
};
|
|
230
|
-
//
|
|
269
|
+
// Order: sign assertion (if SP wants) → encrypt (if IdP wants) → sign message (if needed).
|
|
231
270
|
if (metadata.sp.isWantAssertionsSigned()) {
|
|
232
|
-
// console.debug('sp wants assertion signed');
|
|
233
271
|
rawSamlResponse = libsaml_1.default.constructSAMLSignature(__assign(__assign({}, config), { rawSamlMessage: rawSamlResponse, transformationAlgorithms: spSetting.transformationAlgorithms, referenceTagXPath: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']", signatureConfig: {
|
|
234
272
|
prefix: 'ds',
|
|
235
273
|
location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']/*[local-name(.)='Issuer']", action: 'after' },
|
|
236
274
|
} }));
|
|
237
275
|
}
|
|
238
|
-
// console.debug('after assertion signed', rawSamlResponse);
|
|
239
|
-
// SAML response must be signed sign message first, then encrypt
|
|
240
276
|
if (!encryptThenSign && (spSetting.wantMessageSigned || !metadata.sp.isWantAssertionsSigned())) {
|
|
241
|
-
// console.debug('sign then encrypt and sign entire message');
|
|
242
277
|
rawSamlResponse = libsaml_1.default.constructSAMLSignature(__assign(__assign({}, config), { rawSamlMessage: rawSamlResponse, isMessageSigned: true, transformationAlgorithms: spSetting.transformationAlgorithms, signatureConfig: spSetting.signatureConfig || {
|
|
243
278
|
prefix: 'ds',
|
|
244
279
|
location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Issuer']", action: 'after' },
|
|
@@ -247,17 +282,15 @@ function base64LoginResponse() {
|
|
|
247
282
|
if (!idpSetting.isAssertionEncrypted) return [3 /*break*/, 2];
|
|
248
283
|
return [4 /*yield*/, libsaml_1.default.encryptAssertion(entity.idp, entity.sp, rawSamlResponse)];
|
|
249
284
|
case 1:
|
|
250
|
-
context =
|
|
285
|
+
context = _k.sent();
|
|
251
286
|
if (encryptThenSign) {
|
|
252
|
-
//need to decode it
|
|
253
287
|
rawSamlResponse = utility_1.default.base64Decode(context);
|
|
254
288
|
}
|
|
255
289
|
else {
|
|
256
290
|
return [2 /*return*/, Promise.resolve({ id: id, context: context })];
|
|
257
291
|
}
|
|
258
|
-
|
|
292
|
+
_k.label = 2;
|
|
259
293
|
case 2:
|
|
260
|
-
//sign after encrypting
|
|
261
294
|
if (encryptThenSign && (spSetting.wantMessageSigned || !metadata.sp.isWantAssertionsSigned())) {
|
|
262
295
|
rawSamlResponse = libsaml_1.default.constructSAMLSignature(__assign(__assign({}, config), { rawSamlMessage: rawSamlResponse, isMessageSigned: true, transformationAlgorithms: spSetting.transformationAlgorithms, signatureConfig: spSetting.signatureConfig || {
|
|
263
296
|
prefix: 'ds',
|
|
@@ -268,134 +301,155 @@ function base64LoginResponse() {
|
|
|
268
301
|
id: id,
|
|
269
302
|
context: utility_1.default.base64Encode(rawSamlResponse),
|
|
270
303
|
})];
|
|
271
|
-
case 3: throw new Error('ERR_GENERATE_POST_LOGIN_RESPONSE_MISSING_METADATA');
|
|
272
304
|
}
|
|
273
305
|
});
|
|
274
306
|
});
|
|
275
307
|
}
|
|
276
308
|
/**
|
|
277
|
-
*
|
|
278
|
-
*
|
|
279
|
-
* @param
|
|
280
|
-
* @param
|
|
281
|
-
* @param
|
|
282
|
-
* @
|
|
283
|
-
|
|
309
|
+
* Generate a base64-encoded LogoutRequest for the HTTP-POST binding.
|
|
310
|
+
*
|
|
311
|
+
* @param user currently authenticated user
|
|
312
|
+
* @param referenceTagXPath XPath used when signing the request
|
|
313
|
+
* @param entity `{ init, target }` handles
|
|
314
|
+
* @param customTagReplacement optional custom template transformer
|
|
315
|
+
* @returns id / base64-XML pair
|
|
316
|
+
*/
|
|
284
317
|
function base64LogoutRequest(user, referenceTagXPath, entity, customTagReplacement) {
|
|
318
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
285
319
|
var metadata = { init: entity.init.entityMeta, target: entity.target.entityMeta };
|
|
286
320
|
var initSetting = entity.init.entitySetting;
|
|
287
321
|
var nameIDFormat = initSetting.nameIDFormat;
|
|
288
322
|
var selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
|
|
289
323
|
var id = '';
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
location: { reference: "/*[local-name(.)='LogoutRequest']/*[local-name(.)='Issuer']", action: 'after' },
|
|
326
|
-
}
|
|
327
|
-
}),
|
|
328
|
-
};
|
|
329
|
-
}
|
|
324
|
+
/* v8 ignore start */
|
|
325
|
+
if (!metadata.init || !metadata.target) {
|
|
326
|
+
throw new Error('ERR_GENERATE_POST_LOGOUT_REQUEST_MISSING_METADATA');
|
|
327
|
+
}
|
|
328
|
+
/* v8 ignore stop */
|
|
329
|
+
var rawSamlRequest;
|
|
330
|
+
if (customTagReplacement) {
|
|
331
|
+
// saml-bindings §3.5 — honour the callback even when the caller did
|
|
332
|
+
// not override `logoutRequestTemplate` (closes #549). Prefer the
|
|
333
|
+
// user-supplied template, then the tag-prefixed default (closes #388),
|
|
334
|
+
// and finally the library default.
|
|
335
|
+
var templateContext = (_e = (_b = (_a = initSetting.logoutRequestTemplate) === null || _a === void 0 ? void 0 : _a.context) !== null && _b !== void 0 ? _b : (_d = (_c = initSetting.tagPrefixedDefaults) === null || _c === void 0 ? void 0 : _c.logoutRequestTemplate) === null || _d === void 0 ? void 0 : _d.context) !== null && _e !== void 0 ? _e : libsaml_1.default.defaultLogoutRequestTemplate.context;
|
|
336
|
+
var template = customTagReplacement(templateContext);
|
|
337
|
+
id = (0, utility_1.get)(template, 'id');
|
|
338
|
+
rawSamlRequest = (0, utility_1.get)(template, 'context');
|
|
339
|
+
}
|
|
340
|
+
else {
|
|
341
|
+
id = initSetting.generateID();
|
|
342
|
+
var tvalue = {
|
|
343
|
+
ID: id,
|
|
344
|
+
Destination: metadata.target.getSingleLogoutService(binding.post),
|
|
345
|
+
Issuer: metadata.init.getEntityID(),
|
|
346
|
+
IssueInstant: new Date().toISOString(),
|
|
347
|
+
EntityID: metadata.init.getEntityID(),
|
|
348
|
+
NameIDFormat: selectedNameIDFormat,
|
|
349
|
+
NameID: user.logoutNameID,
|
|
350
|
+
// saml-core §3.7.1 — SessionIndex is optional; replaceTagsByValue
|
|
351
|
+
// drops the element when undefined (closes #470).
|
|
352
|
+
SessionIndex: user.sessionIndex,
|
|
353
|
+
};
|
|
354
|
+
var baseTemplate = (_h = (_g = (_f = initSetting.tagPrefixedDefaults) === null || _f === void 0 ? void 0 : _f.logoutRequestTemplate) === null || _g === void 0 ? void 0 : _g.context) !== null && _h !== void 0 ? _h : libsaml_1.default.defaultLogoutRequestTemplate.context;
|
|
355
|
+
rawSamlRequest = libsaml_1.default.replaceTagsByValue(baseTemplate, tvalue);
|
|
356
|
+
}
|
|
357
|
+
if (entity.target.entitySetting.wantLogoutRequestSigned) {
|
|
358
|
+
var privateKey = initSetting.privateKey, privateKeyPass = initSetting.privateKeyPass, signatureAlgorithm = initSetting.requestSignatureAlgorithm, transformationAlgorithms = initSetting.transformationAlgorithms;
|
|
330
359
|
return {
|
|
331
360
|
id: id,
|
|
332
|
-
context:
|
|
361
|
+
context: libsaml_1.default.constructSAMLSignature({
|
|
362
|
+
referenceTagXPath: referenceTagXPath,
|
|
363
|
+
privateKey: privateKey,
|
|
364
|
+
privateKeyPass: privateKeyPass,
|
|
365
|
+
signatureAlgorithm: signatureAlgorithm,
|
|
366
|
+
transformationAlgorithms: transformationAlgorithms,
|
|
367
|
+
rawSamlMessage: rawSamlRequest,
|
|
368
|
+
signingCert: metadata.init.getX509Certificate('signing'),
|
|
369
|
+
signatureConfig: initSetting.signatureConfig || {
|
|
370
|
+
prefix: 'ds',
|
|
371
|
+
location: { reference: "/*[local-name(.)='LogoutRequest']/*[local-name(.)='Issuer']", action: 'after' },
|
|
372
|
+
},
|
|
373
|
+
}),
|
|
333
374
|
};
|
|
334
375
|
}
|
|
335
|
-
|
|
376
|
+
return {
|
|
377
|
+
id: id,
|
|
378
|
+
context: utility_1.default.base64Encode(rawSamlRequest),
|
|
379
|
+
};
|
|
336
380
|
}
|
|
337
381
|
/**
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
* @param
|
|
341
|
-
* @param
|
|
342
|
-
* @param
|
|
343
|
-
|
|
382
|
+
* Generate a base64-encoded LogoutResponse for the HTTP-POST binding.
|
|
383
|
+
*
|
|
384
|
+
* @param requestInfo parsed request used to link `InResponseTo`
|
|
385
|
+
* @param entity `{ init, target }` handles
|
|
386
|
+
* @param customTagReplacement optional custom template transformer
|
|
387
|
+
* @returns id / base64-XML pair
|
|
388
|
+
*/
|
|
344
389
|
function base64LogoutResponse(requestInfo, entity, customTagReplacement) {
|
|
390
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
345
391
|
var metadata = {
|
|
346
392
|
init: entity.init.entityMeta,
|
|
347
393
|
target: entity.target.entityMeta,
|
|
348
394
|
};
|
|
349
395
|
var id = '';
|
|
350
396
|
var initSetting = entity.init.entitySetting;
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
signatureConfig: {
|
|
384
|
-
prefix: 'ds',
|
|
385
|
-
location: {
|
|
386
|
-
reference: "/*[local-name(.)='LogoutResponse']/*[local-name(.)='Issuer']",
|
|
387
|
-
action: 'after'
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
}),
|
|
391
|
-
};
|
|
392
|
-
}
|
|
397
|
+
/* v8 ignore start */
|
|
398
|
+
if (!metadata.init || !metadata.target) {
|
|
399
|
+
throw new Error('ERR_GENERATE_POST_LOGOUT_RESPONSE_MISSING_METADATA');
|
|
400
|
+
}
|
|
401
|
+
/* v8 ignore stop */
|
|
402
|
+
var rawSamlResponse;
|
|
403
|
+
if (customTagReplacement) {
|
|
404
|
+
// saml-bindings §3.5 — honour the callback even when the caller did
|
|
405
|
+
// not override `logoutResponseTemplate` (closes #549). Prefer the
|
|
406
|
+
// user-supplied template, then the tag-prefixed default (closes #388),
|
|
407
|
+
// and finally the library default.
|
|
408
|
+
var templateContext = (_e = (_b = (_a = initSetting.logoutResponseTemplate) === null || _a === void 0 ? void 0 : _a.context) !== null && _b !== void 0 ? _b : (_d = (_c = initSetting.tagPrefixedDefaults) === null || _c === void 0 ? void 0 : _c.logoutResponseTemplate) === null || _d === void 0 ? void 0 : _d.context) !== null && _e !== void 0 ? _e : libsaml_1.default.defaultLogoutResponseTemplate.context;
|
|
409
|
+
var template = customTagReplacement(templateContext);
|
|
410
|
+
id = template.id;
|
|
411
|
+
rawSamlResponse = template.context;
|
|
412
|
+
}
|
|
413
|
+
else {
|
|
414
|
+
id = initSetting.generateID();
|
|
415
|
+
var tvalue = {
|
|
416
|
+
ID: id,
|
|
417
|
+
Destination: metadata.target.getSingleLogoutService(binding.post),
|
|
418
|
+
EntityID: metadata.init.getEntityID(),
|
|
419
|
+
Issuer: metadata.init.getEntityID(),
|
|
420
|
+
IssueInstant: new Date().toISOString(),
|
|
421
|
+
StatusCode: urn_1.StatusCode.Success,
|
|
422
|
+
InResponseTo: (0, utility_1.get)(requestInfo, 'extract.request.id'),
|
|
423
|
+
};
|
|
424
|
+
var baseTemplate = (_h = (_g = (_f = initSetting.tagPrefixedDefaults) === null || _f === void 0 ? void 0 : _f.logoutResponseTemplate) === null || _g === void 0 ? void 0 : _g.context) !== null && _h !== void 0 ? _h : libsaml_1.default.defaultLogoutResponseTemplate.context;
|
|
425
|
+
rawSamlResponse = libsaml_1.default.replaceTagsByValue(baseTemplate, tvalue);
|
|
426
|
+
}
|
|
427
|
+
if (entity.target.entitySetting.wantLogoutResponseSigned) {
|
|
428
|
+
var privateKey = initSetting.privateKey, privateKeyPass = initSetting.privateKeyPass, signatureAlgorithm = initSetting.requestSignatureAlgorithm, transformationAlgorithms = initSetting.transformationAlgorithms;
|
|
393
429
|
return {
|
|
394
430
|
id: id,
|
|
395
|
-
context:
|
|
431
|
+
context: libsaml_1.default.constructSAMLSignature({
|
|
432
|
+
isMessageSigned: true,
|
|
433
|
+
transformationAlgorithms: transformationAlgorithms,
|
|
434
|
+
privateKey: privateKey,
|
|
435
|
+
privateKeyPass: privateKeyPass,
|
|
436
|
+
signatureAlgorithm: signatureAlgorithm,
|
|
437
|
+
rawSamlMessage: rawSamlResponse,
|
|
438
|
+
signingCert: metadata.init.getX509Certificate('signing'),
|
|
439
|
+
signatureConfig: {
|
|
440
|
+
prefix: 'ds',
|
|
441
|
+
location: {
|
|
442
|
+
reference: "/*[local-name(.)='LogoutResponse']/*[local-name(.)='Issuer']",
|
|
443
|
+
action: 'after',
|
|
444
|
+
},
|
|
445
|
+
},
|
|
446
|
+
}),
|
|
396
447
|
};
|
|
397
448
|
}
|
|
398
|
-
|
|
449
|
+
return {
|
|
450
|
+
id: id,
|
|
451
|
+
context: utility_1.default.base64Encode(rawSamlResponse),
|
|
452
|
+
};
|
|
399
453
|
}
|
|
400
454
|
var postBinding = {
|
|
401
455
|
base64LoginRequest: base64LoginRequest,
|