samlify 2.11.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.
Files changed (83) hide show
  1. package/README.md +1 -1
  2. package/build/src/api.js +52 -3
  3. package/build/src/api.js.map +1 -1
  4. package/build/src/binding-post.js +236 -182
  5. package/build/src/binding-post.js.map +1 -1
  6. package/build/src/binding-redirect.js +303 -215
  7. package/build/src/binding-redirect.js.map +1 -1
  8. package/build/src/binding-simplesign.js +285 -137
  9. package/build/src/binding-simplesign.js.map +1 -1
  10. package/build/src/entity-idp.js +130 -47
  11. package/build/src/entity-idp.js.map +1 -1
  12. package/build/src/entity-sp.js +81 -39
  13. package/build/src/entity-sp.js.map +1 -1
  14. package/build/src/entity.js +100 -62
  15. package/build/src/entity.js.map +1 -1
  16. package/build/src/extractor.js +119 -155
  17. package/build/src/extractor.js.map +1 -1
  18. package/build/src/flow.js +100 -96
  19. package/build/src/flow.js.map +1 -1
  20. package/build/src/libsaml.js +318 -261
  21. package/build/src/libsaml.js.map +1 -1
  22. package/build/src/metadata-idp.js +60 -30
  23. package/build/src/metadata-idp.js.map +1 -1
  24. package/build/src/metadata-sp.js +51 -41
  25. package/build/src/metadata-sp.js.map +1 -1
  26. package/build/src/metadata.js +47 -43
  27. package/build/src/metadata.js.map +1 -1
  28. package/build/src/options.js +73 -0
  29. package/build/src/options.js.map +1 -0
  30. package/build/src/urn.js +28 -1
  31. package/build/src/urn.js.map +1 -1
  32. package/build/src/utility.js +165 -83
  33. package/build/src/utility.js.map +1 -1
  34. package/build/src/validator.js +27 -10
  35. package/build/src/validator.js.map +1 -1
  36. package/package.json +17 -7
  37. package/types/src/api.d.ts +33 -3
  38. package/types/src/binding-post.d.ts +67 -34
  39. package/types/src/binding-redirect.d.ts +58 -31
  40. package/types/src/binding-simplesign.d.ts +77 -21
  41. package/types/src/entity-idp.d.ts +40 -31
  42. package/types/src/entity-sp.d.ts +37 -27
  43. package/types/src/entity.d.ts +71 -77
  44. package/types/src/extractor.d.ts +31 -22
  45. package/types/src/flow.d.ts +24 -2
  46. package/types/src/libsaml.d.ts +172 -118
  47. package/types/src/metadata-idp.d.ts +27 -11
  48. package/types/src/metadata-sp.d.ts +29 -19
  49. package/types/src/metadata.d.ts +59 -34
  50. package/types/src/options.d.ts +37 -0
  51. package/types/src/types.d.ts +250 -24
  52. package/types/src/urn.d.ts +7 -0
  53. package/types/src/utility.d.ts +144 -89
  54. package/types/src/validator.d.ts +21 -0
  55. package/.circleci/config.yml +0 -98
  56. package/.editorconfig +0 -19
  57. package/.github/FUNDING.yml +0 -1
  58. package/.github/workflows/deploy-docs.yml +0 -56
  59. package/.pre-commit.sh +0 -15
  60. package/.snyk +0 -4
  61. package/Makefile +0 -25
  62. package/index.ts +0 -28
  63. package/src/api.ts +0 -36
  64. package/src/binding-post.ts +0 -336
  65. package/src/binding-redirect.ts +0 -335
  66. package/src/binding-simplesign.ts +0 -231
  67. package/src/entity-idp.ts +0 -145
  68. package/src/entity-sp.ts +0 -114
  69. package/src/entity.ts +0 -243
  70. package/src/extractor.ts +0 -399
  71. package/src/flow.ts +0 -469
  72. package/src/libsaml.ts +0 -777
  73. package/src/metadata-idp.ts +0 -146
  74. package/src/metadata-sp.ts +0 -203
  75. package/src/metadata.ts +0 -166
  76. package/src/types.ts +0 -127
  77. package/src/urn.ts +0 -210
  78. package/src/utility.ts +0 -231
  79. package/src/validator.ts +0 -44
  80. package/tsconfig.json +0 -41
  81. package/tslint.json +0 -35
  82. package/types.d.ts +0 -2
  83. package/vitest.config.ts +0 -12
@@ -1,231 +0,0 @@
1
- /**
2
- * @file binding-simplesign.ts
3
- * @author Orange
4
- * @desc Binding-level API, declare the functions using POST SimpleSign binding
5
- */
6
-
7
- import { wording, StatusCode } from './urn';
8
- import { BindingContext, SimpleSignComputedContext } from './entity';
9
- import libsaml from './libsaml';
10
- import utility, { get } from './utility';
11
-
12
- const binding = wording.binding;
13
- const urlParams = wording.urlParams;
14
-
15
- export interface BuildSimpleSignConfig {
16
- type: string;
17
- context: string;
18
- entitySetting: any;
19
- relayState?: string;
20
- }
21
-
22
- export interface BindingSimpleSignContext {
23
- id: string;
24
- context: string;
25
- signature: any;
26
- sigAlg: string;
27
- }
28
-
29
- /**
30
- * @private
31
- * @desc Helper of generating URL param/value pair
32
- * @param {string} param key
33
- * @param {string} value value of key
34
- * @param {boolean} first determine whether the param is the starting one in order to add query header '?'
35
- * @return {string}
36
- */
37
- function pvPair(param: string, value: string, first?: boolean): string {
38
- return (first === true ? '?' : '&') + param + '=' + value;
39
- }
40
- /**
41
- * @private
42
- * @desc Refactored part of simple signature generation for login/logout request
43
- * @param {string} type
44
- * @param {string} rawSamlRequest
45
- * @param {object} entitySetting
46
- * @return {string}
47
- */
48
- function buildSimpleSignature(opts: BuildSimpleSignConfig) : string {
49
- const {
50
- type,
51
- context,
52
- entitySetting,
53
- } = opts;
54
- let { relayState = '' } = opts;
55
- const queryParam = libsaml.getQueryParamByType(type);
56
-
57
- if (relayState !== '') {
58
- relayState = pvPair(urlParams.relayState, relayState);
59
- }
60
-
61
- const sigAlg = pvPair(urlParams.sigAlg, entitySetting.requestSignatureAlgorithm);
62
- const octetString = context + relayState + sigAlg;
63
- return libsaml.constructMessageSignature(
64
- queryParam + '=' + octetString,
65
- entitySetting.privateKey,
66
- entitySetting.privateKeyPass,
67
- undefined,
68
- entitySetting.requestSignatureAlgorithm
69
- ).toString();
70
- }
71
-
72
- /**
73
- * @desc Generate a base64 encoded login request
74
- * @param {string} referenceTagXPath reference uri
75
- * @param {object} entity object includes both idp and sp
76
- * @param {function} customTagReplacement used when developers have their own login response template
77
- */
78
- function base64LoginRequest(entity: any, customTagReplacement?: (template: string) => BindingContext): SimpleSignComputedContext {
79
- const metadata = { idp: entity.idp.entityMeta, sp: entity.sp.entityMeta };
80
- const spSetting = entity.sp.entitySetting;
81
- let id: string = '';
82
-
83
- if (metadata && metadata.idp && metadata.sp) {
84
- const base = metadata.idp.getSingleSignOnService(binding.simpleSign);
85
- let rawSamlRequest: string;
86
- if (spSetting.loginRequestTemplate && customTagReplacement) {
87
- const info = customTagReplacement(spSetting.loginRequestTemplate.context);
88
- id = get(info, 'id', null);
89
- rawSamlRequest = get(info, 'context', null);
90
- } else {
91
- const nameIDFormat = spSetting.nameIDFormat;
92
- const selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
93
- id = spSetting.generateID();
94
- rawSamlRequest = libsaml.replaceTagsByValue(libsaml.defaultLoginRequestTemplate.context, {
95
- ID: id,
96
- Destination: base,
97
- Issuer: metadata.sp.getEntityID(),
98
- IssueInstant: new Date().toISOString(),
99
- AssertionConsumerServiceURL: metadata.sp.getAssertionConsumerService(binding.simpleSign),
100
- EntityID: metadata.sp.getEntityID(),
101
- AllowCreate: spSetting.allowCreate,
102
- NameIDFormat: selectedNameIDFormat
103
- } as any);
104
- }
105
-
106
- let simpleSignatureContext : any = null;
107
- if (metadata.idp.isWantAuthnRequestsSigned()) {
108
- const simpleSignature = buildSimpleSignature({
109
- type: urlParams.samlRequest,
110
- context: rawSamlRequest,
111
- entitySetting: spSetting,
112
- relayState: spSetting.relayState,
113
- });
114
-
115
- simpleSignatureContext = {
116
- signature: simpleSignature,
117
- sigAlg: spSetting.requestSignatureAlgorithm,
118
- };
119
- }
120
- // No need to embeded XML signature
121
- return {
122
- id,
123
- context: utility.base64Encode(rawSamlRequest),
124
- ...simpleSignatureContext,
125
- };
126
- }
127
- throw new Error('ERR_GENERATE_POST_SIMPLESIGN_LOGIN_REQUEST_MISSING_METADATA');
128
- }
129
- /**
130
- * @desc Generate a base64 encoded login response
131
- * @param {object} requestInfo corresponding request, used to obtain the id
132
- * @param {object} entity object includes both idp and sp
133
- * @param {object} user current logged user (e.g. req.user)
134
- * @param {string} relayState the relay state
135
- * @param {function} customTagReplacement used when developers have their own login response template
136
- */
137
- async function base64LoginResponse(requestInfo: any = {}, entity: any, user: any = {}, relayState?: string, customTagReplacement?: (template: string) => BindingContext): Promise<BindingSimpleSignContext> {
138
- const idpSetting = entity.idp.entitySetting;
139
- const spSetting = entity.sp.entitySetting;
140
- const id = idpSetting.generateID();
141
- const metadata = {
142
- idp: entity.idp.entityMeta,
143
- sp: entity.sp.entityMeta,
144
- };
145
- const nameIDFormat = idpSetting.nameIDFormat;
146
- const selectedNameIDFormat = Array.isArray(nameIDFormat) ? nameIDFormat[0] : nameIDFormat;
147
- if (metadata && metadata.idp && metadata.sp) {
148
- const base = metadata.sp.getAssertionConsumerService(binding.simpleSign);
149
- let rawSamlResponse: string;
150
- const nowTime = new Date();
151
- // Five minutes later : nowtime + 5 * 60 * 1000 (in milliseconds)
152
- const fiveMinutesLaterTime = new Date(nowTime.getTime() + 300_000 );
153
- const tvalue: any = {
154
- ID: id,
155
- AssertionID: idpSetting.generateID(),
156
- Destination: base,
157
- Audience: metadata.sp.getEntityID(),
158
- EntityID: metadata.sp.getEntityID(),
159
- SubjectRecipient: base,
160
- Issuer: metadata.idp.getEntityID(),
161
- IssueInstant: nowTime.toISOString(),
162
- AssertionConsumerServiceURL: base,
163
- StatusCode: StatusCode.Success,
164
- // can be customized
165
- ConditionsNotBefore: nowTime.toISOString(),
166
- ConditionsNotOnOrAfter: fiveMinutesLaterTime.toISOString(),
167
- SubjectConfirmationDataNotOnOrAfter: fiveMinutesLaterTime.toISOString(),
168
- NameIDFormat: selectedNameIDFormat,
169
- NameID: user.email || '',
170
- InResponseTo: get(requestInfo, 'extract.request.id', ''),
171
- AuthnStatement: '',
172
- AttributeStatement: '',
173
- };
174
- if (idpSetting.loginResponseTemplate && customTagReplacement) {
175
- const template = customTagReplacement(idpSetting.loginResponseTemplate.context);
176
- rawSamlResponse = get(template, 'context', null);
177
- } else {
178
- if (requestInfo !== null) {
179
- tvalue.InResponseTo = requestInfo.extract.request.id;
180
- }
181
- rawSamlResponse = libsaml.replaceTagsByValue(libsaml.defaultLoginResponseTemplate.context, tvalue);
182
- }
183
- const { privateKey, privateKeyPass, requestSignatureAlgorithm: signatureAlgorithm } = idpSetting;
184
- const config = {
185
- privateKey,
186
- privateKeyPass,
187
- signatureAlgorithm,
188
- signingCert: metadata.idp.getX509Certificate('signing'),
189
- isBase64Output: false,
190
- };
191
- // step: sign assertion ? -> encrypted ? -> sign message ?
192
- if (metadata.sp.isWantAssertionsSigned()) {
193
- rawSamlResponse = libsaml.constructSAMLSignature({
194
- ...config,
195
- rawSamlMessage: rawSamlResponse,
196
- transformationAlgorithms: spSetting.transformationAlgorithms,
197
- referenceTagXPath: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']",
198
- signatureConfig: {
199
- prefix: 'ds',
200
- location: { reference: "/*[local-name(.)='Response']/*[local-name(.)='Assertion']/*[local-name(.)='Issuer']", action: 'after' },
201
- },
202
- });
203
- }
204
-
205
- // SAML response must be signed sign message first, then encrypt
206
- let simpleSignature: string = '';
207
- // like in post and redirect bindings, login response is always signed.
208
- simpleSignature = buildSimpleSignature({
209
- type: urlParams.samlResponse,
210
- context: rawSamlResponse,
211
- entitySetting: idpSetting,
212
- relayState: relayState,
213
- } );
214
-
215
- return Promise.resolve({
216
- id,
217
- context: utility.base64Encode(rawSamlResponse),
218
- signature: simpleSignature,
219
- sigAlg: idpSetting.requestSignatureAlgorithm,
220
- });
221
-
222
- }
223
- throw new Error('ERR_GENERATE_POST_SIMPLESIGN_LOGIN_RESPONSE_MISSING_METADATA');
224
- }
225
-
226
- const simpleSignBinding = {
227
- base64LoginRequest,
228
- base64LoginResponse,
229
- };
230
-
231
- export default simpleSignBinding;
package/src/entity-idp.ts DELETED
@@ -1,145 +0,0 @@
1
- /**
2
- * @file entity-idp.ts
3
- * @author tngan
4
- * @desc Declares the actions taken by identity provider
5
- */
6
- import Entity, { ESamlHttpRequest } from './entity';
7
- import {
8
- ServiceProviderConstructor as ServiceProvider,
9
- ServiceProviderMetadata,
10
- IdentityProviderMetadata,
11
- IdentityProviderSettings,
12
- } from './types';
13
- import libsaml from './libsaml';
14
- import { namespace } from './urn';
15
- import postBinding from './binding-post';
16
- import redirectBinding from './binding-redirect';
17
- import simpleSignBinding from './binding-simplesign';
18
- import { flow, FlowResult } from './flow';
19
- import { isString } from './utility';
20
- import { BindingContext } from './entity';
21
-
22
- /**
23
- * Identity provider can be configured using either metadata importing or idpSetting
24
- */
25
- export default function(props: IdentityProviderSettings) {
26
- return new IdentityProvider(props);
27
- }
28
-
29
- /**
30
- * Identity provider can be configured using either metadata importing or idpSetting
31
- */
32
- export class IdentityProvider extends Entity {
33
-
34
- entityMeta: IdentityProviderMetadata;
35
-
36
- constructor(idpSetting: IdentityProviderSettings) {
37
- const defaultIdpEntitySetting = {
38
- wantAuthnRequestsSigned: false,
39
- tagPrefix: {
40
- encryptedAssertion: 'saml',
41
- },
42
- };
43
- const entitySetting = Object.assign(defaultIdpEntitySetting, idpSetting);
44
- // build attribute part
45
- if (idpSetting.loginResponseTemplate) {
46
- if (isString(idpSetting.loginResponseTemplate.context) && Array.isArray(idpSetting.loginResponseTemplate.attributes)) {
47
- let attributeStatementTemplate;
48
- let attributeTemplate;
49
- if (!idpSetting.loginResponseTemplate.additionalTemplates || !idpSetting.loginResponseTemplate.additionalTemplates!.attributeStatementTemplate) {
50
- attributeStatementTemplate = libsaml.defaultAttributeStatementTemplate;
51
- } else {
52
- attributeStatementTemplate = idpSetting.loginResponseTemplate.additionalTemplates!.attributeStatementTemplate!;
53
- }
54
- if (!idpSetting.loginResponseTemplate.additionalTemplates || !idpSetting.loginResponseTemplate.additionalTemplates!.attributeTemplate) {
55
- attributeTemplate = libsaml.defaultAttributeTemplate;
56
- } else {
57
- attributeTemplate = idpSetting.loginResponseTemplate.additionalTemplates!.attributeTemplate!;
58
- }
59
- const replacement = {
60
- AttributeStatement: libsaml.attributeStatementBuilder(idpSetting.loginResponseTemplate.attributes, attributeTemplate, attributeStatementTemplate),
61
- };
62
- entitySetting.loginResponseTemplate = {
63
- ...entitySetting.loginResponseTemplate,
64
- context: libsaml.replaceTagsByValue(entitySetting.loginResponseTemplate!.context, replacement),
65
- };
66
- } else {
67
- console.warn('Invalid login response template');
68
- }
69
- }
70
- super(entitySetting, 'idp');
71
- }
72
-
73
- /**
74
- * @desc Generates the login response for developers to design their own method
75
- * @param sp object of service provider
76
- * @param requestInfo corresponding request, used to obtain the id
77
- * @param binding protocol binding
78
- * @param user current logged user (e.g. req.user)
79
- * @param customTagReplacement used when developers have their own login response template
80
- * @param encryptThenSign whether or not to encrypt then sign first (if signing)
81
- * @param relayState the relayState from corresponding request
82
- */
83
- public async createLoginResponse(
84
- sp: ServiceProvider,
85
- requestInfo: { [key: string]: any },
86
- binding: string,
87
- user: { [key: string]: any },
88
- customTagReplacement?: (template: string) => BindingContext,
89
- encryptThenSign?: boolean,
90
- relayState?: string,
91
- ) {
92
- const protocol = namespace.binding[binding];
93
- // can support post, redirect and post simple sign bindings for login response
94
- let context: any = null;
95
- switch (protocol) {
96
- case namespace.binding.post:
97
- context = await postBinding.base64LoginResponse(requestInfo, {
98
- idp: this,
99
- sp,
100
- }, user, customTagReplacement, encryptThenSign);
101
- break;
102
-
103
- case namespace.binding.simpleSign:
104
- context = await simpleSignBinding.base64LoginResponse( requestInfo, {
105
- idp: this, sp,
106
- }, user, relayState, customTagReplacement);
107
- break;
108
-
109
- case namespace.binding.redirect:
110
- return redirectBinding.loginResponseRedirectURL(requestInfo, {
111
- idp: this,
112
- sp,
113
- }, user, relayState, customTagReplacement);
114
-
115
- default:
116
- throw new Error('ERR_CREATE_RESPONSE_UNDEFINED_BINDING');
117
- }
118
-
119
- return {
120
- ...context,
121
- relayState,
122
- entityEndpoint: (sp.entityMeta as ServiceProviderMetadata).getAssertionConsumerService(binding) as string,
123
- type: 'SAMLResponse'
124
- };
125
- }
126
-
127
- /**
128
- * Validation of the parsed URL parameters
129
- * @param sp ServiceProvider instance
130
- * @param binding Protocol binding
131
- * @param req RequesmessageSigningOrderst
132
- */
133
- parseLoginRequest(sp: ServiceProvider, binding: string, req: ESamlHttpRequest) {
134
- const self = this;
135
- return flow({
136
- from: sp,
137
- self: self,
138
- checkSignature: self.entityMeta.isWantAuthnRequestsSigned(),
139
- parserType: 'SAMLRequest',
140
- type: 'login',
141
- binding: binding,
142
- request: req
143
- });
144
- }
145
- }
package/src/entity-sp.ts DELETED
@@ -1,114 +0,0 @@
1
- /**
2
- * @file entity-sp.ts
3
- * @author tngan
4
- * @desc Declares the actions taken by service provider
5
- */
6
- import Entity, {
7
- BindingContext,
8
- PostBindingContext,
9
- ESamlHttpRequest,
10
- SimpleSignBindingContext,
11
- } from './entity';
12
- import {
13
- IdentityProviderConstructor as IdentityProvider,
14
- ServiceProviderMetadata,
15
- ServiceProviderSettings,
16
- } from './types';
17
- import { namespace } from './urn';
18
- import redirectBinding from './binding-redirect';
19
- import postBinding from './binding-post';
20
- import simpleSignBinding from './binding-simplesign';
21
- import { flow, FlowResult } from './flow';
22
-
23
- /*
24
- * @desc interface function
25
- */
26
- export default function(props: ServiceProviderSettings) {
27
- return new ServiceProvider(props);
28
- }
29
-
30
- /**
31
- * @desc Service provider can be configured using either metadata importing or spSetting
32
- * @param {object} spSettingimport { FlowResult } from '../types/src/flow.d';
33
-
34
- */
35
- export class ServiceProvider extends Entity {
36
- entityMeta: ServiceProviderMetadata;
37
-
38
- /**
39
- * @desc Inherited from Entity
40
- * @param {object} spSetting setting of service provider
41
- */
42
- constructor(spSetting: ServiceProviderSettings) {
43
- const entitySetting = Object.assign({
44
- authnRequestsSigned: false,
45
- wantAssertionsSigned: false,
46
- wantMessageSigned: false,
47
- }, spSetting);
48
- super(entitySetting, 'sp');
49
- }
50
-
51
- /**
52
- * @desc Generates the login request for developers to design their own method
53
- * @param {IdentityProvider} idp object of identity provider
54
- * @param {string} binding protocol binding
55
- * @param {function} customTagReplacement used when developers have their own login response template
56
- */
57
- public createLoginRequest(
58
- idp: IdentityProvider,
59
- binding = 'redirect',
60
- customTagReplacement?: (template: string) => BindingContext,
61
- ): BindingContext | PostBindingContext| SimpleSignBindingContext {
62
- const nsBinding = namespace.binding;
63
- const protocol = nsBinding[binding];
64
- if (this.entityMeta.isAuthnRequestSigned() !== idp.entityMeta.isWantAuthnRequestsSigned()) {
65
- throw new Error('ERR_METADATA_CONFLICT_REQUEST_SIGNED_FLAG');
66
- }
67
-
68
- let context: any = null;
69
- switch (protocol) {
70
- case nsBinding.redirect:
71
- return redirectBinding.loginRequestRedirectURL({ idp, sp: this }, customTagReplacement);
72
-
73
- case nsBinding.post:
74
- context = postBinding.base64LoginRequest("/*[local-name(.)='AuthnRequest']", { idp, sp: this }, customTagReplacement);
75
- break;
76
-
77
- case nsBinding.simpleSign:
78
- // Object context = {id, context, signature, sigAlg}
79
- context = simpleSignBinding.base64LoginRequest( { idp, sp: this }, customTagReplacement);
80
- break;
81
-
82
- default:
83
- // Will support artifact in the next release
84
- throw new Error('ERR_SP_LOGIN_REQUEST_UNDEFINED_BINDING');
85
- }
86
-
87
- return {
88
- ...context,
89
- relayState: this.entitySetting.relayState,
90
- entityEndpoint: idp.entityMeta.getSingleSignOnService(binding) as string,
91
- type: 'SAMLRequest',
92
- };
93
- }
94
-
95
- /**
96
- * @desc Validation of the parsed the URL parameters
97
- * @param {IdentityProvider} idp object of identity provider
98
- * @param {string} binding protocol binding
99
- * @param {request} req request
100
- */
101
- public parseLoginResponse(idp, binding, request: ESamlHttpRequest) {
102
- const self = this;
103
- return flow({
104
- from: idp,
105
- self: self,
106
- checkSignature: true, // saml response must have signature
107
- parserType: 'SAMLResponse',
108
- type: 'login',
109
- binding: binding,
110
- request: request
111
- });
112
- }
113
-
114
- }