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.
Files changed (84) hide show
  1. package/README.md +1 -1
  2. package/build/src/api.js +41 -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 +118 -151
  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 +315 -259
  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 +140 -85
  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 +16 -5
  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 +139 -90
  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/samlify-2.11.0.tgz +0 -0
  64. package/src/api.ts +0 -48
  65. package/src/binding-post.ts +0 -336
  66. package/src/binding-redirect.ts +0 -335
  67. package/src/binding-simplesign.ts +0 -231
  68. package/src/entity-idp.ts +0 -145
  69. package/src/entity-sp.ts +0 -114
  70. package/src/entity.ts +0 -243
  71. package/src/extractor.ts +0 -399
  72. package/src/flow.ts +0 -469
  73. package/src/libsaml.ts +0 -779
  74. package/src/metadata-idp.ts +0 -146
  75. package/src/metadata-sp.ts +0 -203
  76. package/src/metadata.ts +0 -166
  77. package/src/types.ts +0 -127
  78. package/src/urn.ts +0 -210
  79. package/src/utility.ts +0 -259
  80. package/src/validator.ts +0 -44
  81. package/tsconfig.json +0 -41
  82. package/tslint.json +0 -35
  83. package/types.d.ts +0 -2
  84. 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
- }