@tachybase/plugin-auth-saml 0.23.8

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 (67) hide show
  1. package/.turbo/turbo-build.log +14 -0
  2. package/README.md +11 -0
  3. package/README.zh-CN.md +55 -0
  4. package/client.d.ts +2 -0
  5. package/client.js +1 -0
  6. package/dist/client/Options.d.ts +2 -0
  7. package/dist/client/SAMLButton.d.ts +5 -0
  8. package/dist/client/index.d.ts +5 -0
  9. package/dist/client/index.js +3 -0
  10. package/dist/client/locale/index.d.ts +3 -0
  11. package/dist/client/schemas/saml.d.ts +35 -0
  12. package/dist/constants.d.ts +2 -0
  13. package/dist/constants.js +31 -0
  14. package/dist/externalVersion.js +14 -0
  15. package/dist/index.d.ts +2 -0
  16. package/dist/index.js +39 -0
  17. package/dist/locale/en-US.json +26 -0
  18. package/dist/locale/es-ES.json +22 -0
  19. package/dist/locale/fr-FR.json +22 -0
  20. package/dist/locale/ko_KR.json +29 -0
  21. package/dist/locale/pt-BR.json +22 -0
  22. package/dist/locale/zh-CN.json +29 -0
  23. package/dist/node_modules/@node-saml/node-saml/LICENSE +23 -0
  24. package/dist/node_modules/@node-saml/node-saml/lib/algorithms.d.ts +5 -0
  25. package/dist/node_modules/@node-saml/node-saml/lib/algorithms.js +41 -0
  26. package/dist/node_modules/@node-saml/node-saml/lib/crypto.d.ts +5 -0
  27. package/dist/node_modules/@node-saml/node-saml/lib/crypto.js +48 -0
  28. package/dist/node_modules/@node-saml/node-saml/lib/datetime.d.ts +13 -0
  29. package/dist/node_modules/@node-saml/node-saml/lib/datetime.js +27 -0
  30. package/dist/node_modules/@node-saml/node-saml/lib/index.d.ts +3 -0
  31. package/dist/node_modules/@node-saml/node-saml/lib/index.js +9 -0
  32. package/dist/node_modules/@node-saml/node-saml/lib/inmemory-cache-provider.d.ts +38 -0
  33. package/dist/node_modules/@node-saml/node-saml/lib/inmemory-cache-provider.js +100 -0
  34. package/dist/node_modules/@node-saml/node-saml/lib/metadata.d.ts +2 -0
  35. package/dist/node_modules/@node-saml/node-saml/lib/metadata.js +112 -0
  36. package/dist/node_modules/@node-saml/node-saml/lib/passport-saml-types.d.ts +8 -0
  37. package/dist/node_modules/@node-saml/node-saml/lib/passport-saml-types.js +3 -0
  38. package/dist/node_modules/@node-saml/node-saml/lib/saml-post-signing.d.ts +3 -0
  39. package/dist/node_modules/@node-saml/node-saml/lib/saml-post-signing.js +15 -0
  40. package/dist/node_modules/@node-saml/node-saml/lib/saml.d.ts +75 -0
  41. package/dist/node_modules/@node-saml/node-saml/lib/saml.js +1005 -0
  42. package/dist/node_modules/@node-saml/node-saml/lib/types.d.ts +219 -0
  43. package/dist/node_modules/@node-saml/node-saml/lib/types.js +21 -0
  44. package/dist/node_modules/@node-saml/node-saml/lib/utility.d.ts +5 -0
  45. package/dist/node_modules/@node-saml/node-saml/lib/utility.js +27 -0
  46. package/dist/node_modules/@node-saml/node-saml/lib/xml.d.ts +26 -0
  47. package/dist/node_modules/@node-saml/node-saml/lib/xml.js +234 -0
  48. package/dist/node_modules/@node-saml/node-saml/package.json +1 -0
  49. package/dist/server/actions/getAuthUrl.d.ts +2 -0
  50. package/dist/server/actions/getAuthUrl.js +35 -0
  51. package/dist/server/actions/metadata.d.ts +2 -0
  52. package/dist/server/actions/metadata.js +36 -0
  53. package/dist/server/actions/redirect.d.ts +2 -0
  54. package/dist/server/actions/redirect.js +49 -0
  55. package/dist/server/index.d.ts +1 -0
  56. package/dist/server/index.js +33 -0
  57. package/dist/server/migrations/20231008112900-update-autosignup.d.ts +6 -0
  58. package/dist/server/migrations/20231008112900-update-autosignup.js +52 -0
  59. package/dist/server/plugin.d.ts +11 -0
  60. package/dist/server/plugin.js +70 -0
  61. package/dist/server/saml-auth.d.ts +8 -0
  62. package/dist/server/saml-auth.js +110 -0
  63. package/dist/swagger/index.d.ts +137 -0
  64. package/dist/swagger/index.js +163 -0
  65. package/package.json +35 -0
  66. package/server.d.ts +2 -0
  67. package/server.js +1 -0
@@ -0,0 +1,38 @@
1
+ /**
2
+ * Simple in memory cache provider. To be used to store state of requests that needs
3
+ * to be validated/checked when a response is received.
4
+ *
5
+ * This is the default implementation of a cache provider used by Node SAML. For
6
+ * multiple server instances/load balanced scenarios (I.e. the SAML request could have
7
+ * been generated from a different server/process handling the SAML response) this
8
+ * implementation will NOT be sufficient.
9
+ *
10
+ * The caller should provide their own implementation for a cache provider as defined
11
+ * in the config options.
12
+ */
13
+ import { CacheItem, CacheProvider } from "./types";
14
+ interface CacheProviderOptions {
15
+ keyExpirationPeriodMs: number;
16
+ }
17
+ export declare class InMemoryCacheProvider implements CacheProvider {
18
+ private cacheKeys;
19
+ private options;
20
+ private lastPrune;
21
+ private prune;
22
+ private removeKeyIfExpired;
23
+ constructor(options: Partial<CacheProviderOptions>);
24
+ /**
25
+ * Store an item in the cache, using the specified key and value.
26
+ * Internally will keep track of the time the item was added to the cache
27
+ */
28
+ saveAsync(key: string, value: string): Promise<CacheItem | null>;
29
+ /**
30
+ * Returns the value of the specified key in the cache
31
+ */
32
+ getAsync(key: string): Promise<string | null>;
33
+ /**
34
+ * Removes an item from the cache if it exists
35
+ */
36
+ removeAsync(key: string | null): Promise<string | null>;
37
+ }
38
+ export {};
@@ -0,0 +1,100 @@
1
+ "use strict";
2
+ /**
3
+ * Simple in memory cache provider. To be used to store state of requests that needs
4
+ * to be validated/checked when a response is received.
5
+ *
6
+ * This is the default implementation of a cache provider used by Node SAML. For
7
+ * multiple server instances/load balanced scenarios (I.e. the SAML request could have
8
+ * been generated from a different server/process handling the SAML response) this
9
+ * implementation will NOT be sufficient.
10
+ *
11
+ * The caller should provide their own implementation for a cache provider as defined
12
+ * in the config options.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.InMemoryCacheProvider = void 0;
16
+ class InMemoryCacheProvider {
17
+ constructor(options) {
18
+ var _a;
19
+ this.lastPrune = 0;
20
+ this.cacheKeys = {};
21
+ this.options = {
22
+ ...options,
23
+ keyExpirationPeriodMs: (_a = options.keyExpirationPeriodMs) !== null && _a !== void 0 ? _a : 28800000, // 8 hours,
24
+ };
25
+ // Remove expired cache keys
26
+ this.prune = () => {
27
+ const nowMs = new Date().getTime();
28
+ // Don't call this function more than is needed in high-load environments
29
+ if (nowMs > this.lastPrune + this.options.keyExpirationPeriodMs) {
30
+ const keys = Object.keys(this.cacheKeys);
31
+ const keysToRemove = [];
32
+ keys.forEach((key) => {
33
+ if (nowMs >= this.cacheKeys[key].createdAt + this.options.keyExpirationPeriodMs) {
34
+ keysToRemove.push(key);
35
+ }
36
+ });
37
+ // No need to await this because we don't care when it gets done
38
+ keysToRemove.forEach((key) => this.removeAsync(key));
39
+ this.lastPrune = nowMs;
40
+ }
41
+ };
42
+ this.removeKeyIfExpired = async (key, nowMs) => {
43
+ if (this.cacheKeys[key] &&
44
+ nowMs >= this.cacheKeys[key].createdAt + this.options.keyExpirationPeriodMs) {
45
+ await this.removeAsync(key);
46
+ }
47
+ };
48
+ }
49
+ /**
50
+ * Store an item in the cache, using the specified key and value.
51
+ * Internally will keep track of the time the item was added to the cache
52
+ */
53
+ async saveAsync(key, value) {
54
+ // Remove all expired keys at a later time
55
+ this.prune();
56
+ // Remove the key if it is expired
57
+ const nowMs = new Date().getTime();
58
+ await this.removeKeyIfExpired(key, nowMs);
59
+ if (!this.cacheKeys[key]) {
60
+ this.cacheKeys[key] = {
61
+ createdAt: nowMs,
62
+ value: value,
63
+ };
64
+ return this.cacheKeys[key];
65
+ }
66
+ else {
67
+ return null;
68
+ }
69
+ }
70
+ /**
71
+ * Returns the value of the specified key in the cache
72
+ */
73
+ async getAsync(key) {
74
+ // Remove all expired keys at a later time
75
+ this.prune();
76
+ // Remove the key if it is expired
77
+ const nowMs = new Date().getTime();
78
+ await this.removeKeyIfExpired(key, nowMs);
79
+ if (this.cacheKeys[key]) {
80
+ return this.cacheKeys[key].value;
81
+ }
82
+ else {
83
+ return null;
84
+ }
85
+ }
86
+ /**
87
+ * Removes an item from the cache if it exists
88
+ */
89
+ async removeAsync(key) {
90
+ if (key != null && this.cacheKeys[key]) {
91
+ delete this.cacheKeys[key];
92
+ return key;
93
+ }
94
+ else {
95
+ return null;
96
+ }
97
+ }
98
+ }
99
+ exports.InMemoryCacheProvider = InMemoryCacheProvider;
100
+ //# sourceMappingURL=inmemory-cache-provider.js.map
@@ -0,0 +1,2 @@
1
+ import { GenerateServiceProviderMetadataParams } from "./types";
2
+ export declare const generateServiceProviderMetadata: (params: GenerateServiceProviderMetadataParams) => string;
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.generateServiceProviderMetadata = void 0;
4
+ const crypto_1 = require("./crypto");
5
+ const types_1 = require("./types");
6
+ const utility_1 = require("./utility");
7
+ const xml_1 = require("./xml");
8
+ const generateServiceProviderMetadata = (params) => {
9
+ const { issuer, callbackUrl, logoutCallbackUrl, identifierFormat, wantAssertionsSigned, decryptionPvk, privateKey, metadataContactPerson, metadataOrganization, generateUniqueId, } = params;
10
+ let { signingCerts, decryptionCert } = params;
11
+ if (decryptionPvk != null) {
12
+ if (!decryptionCert) {
13
+ throw new Error("Missing decryptionCert while generating metadata for decrypting service provider");
14
+ }
15
+ }
16
+ else {
17
+ decryptionCert = null;
18
+ }
19
+ if (privateKey != null) {
20
+ if (!signingCerts) {
21
+ throw new Error("Missing signingCert while generating metadata for signing service provider messages");
22
+ }
23
+ signingCerts = !Array.isArray(signingCerts) ? [signingCerts] : signingCerts;
24
+ }
25
+ else {
26
+ signingCerts = null;
27
+ }
28
+ const metadata = {
29
+ EntityDescriptor: {
30
+ "@xmlns": "urn:oasis:names:tc:SAML:2.0:metadata",
31
+ "@xmlns:ds": "http://www.w3.org/2000/09/xmldsig#",
32
+ "@entityID": issuer,
33
+ "@ID": generateUniqueId(),
34
+ SPSSODescriptor: {
35
+ "@protocolSupportEnumeration": "urn:oasis:names:tc:SAML:2.0:protocol",
36
+ "@AuthnRequestsSigned": "false",
37
+ },
38
+ ...(metadataContactPerson ? { ContactPerson: metadataContactPerson } : {}),
39
+ ...(metadataOrganization ? { Organization: metadataOrganization } : {}),
40
+ },
41
+ };
42
+ if (decryptionCert != null || signingCerts != null) {
43
+ metadata.EntityDescriptor.SPSSODescriptor.KeyDescriptor = [];
44
+ if ((0, types_1.isValidSamlSigningOptions)(params)) {
45
+ (0, utility_1.assertRequired)(signingCerts, "Missing signingCert while generating metadata for signing service provider messages");
46
+ metadata.EntityDescriptor.SPSSODescriptor["@AuthnRequestsSigned"] = true;
47
+ const certArray = Array.isArray(signingCerts) ? signingCerts : [signingCerts];
48
+ const signingKeyDescriptors = certArray.map((cert) => ({
49
+ "@use": "signing",
50
+ "ds:KeyInfo": {
51
+ "ds:X509Data": {
52
+ "ds:X509Certificate": {
53
+ "#text": (0, crypto_1.removeCertPEMHeaderAndFooter)(cert),
54
+ },
55
+ },
56
+ },
57
+ }));
58
+ metadata.EntityDescriptor.SPSSODescriptor.KeyDescriptor.push(signingKeyDescriptors);
59
+ }
60
+ if (decryptionPvk != null) {
61
+ (0, utility_1.assertRequired)(decryptionCert, "Missing decryptionCert while generating metadata for decrypting service provider");
62
+ decryptionCert = (0, crypto_1.removeCertPEMHeaderAndFooter)(decryptionCert);
63
+ metadata.EntityDescriptor.SPSSODescriptor.KeyDescriptor.push({
64
+ "@use": "encryption",
65
+ "ds:KeyInfo": {
66
+ "ds:X509Data": {
67
+ "ds:X509Certificate": {
68
+ "#text": decryptionCert,
69
+ },
70
+ },
71
+ },
72
+ EncryptionMethod: [
73
+ // this should be the set that the xmlenc library supports
74
+ { "@Algorithm": "http://www.w3.org/2009/xmlenc11#aes256-gcm" },
75
+ { "@Algorithm": "http://www.w3.org/2009/xmlenc11#aes128-gcm" },
76
+ { "@Algorithm": "http://www.w3.org/2001/04/xmlenc#aes256-cbc" },
77
+ { "@Algorithm": "http://www.w3.org/2001/04/xmlenc#aes128-cbc" },
78
+ ],
79
+ });
80
+ }
81
+ }
82
+ if (logoutCallbackUrl != null) {
83
+ metadata.EntityDescriptor.SPSSODescriptor.SingleLogoutService = {
84
+ "@Binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
85
+ "@Location": logoutCallbackUrl,
86
+ };
87
+ }
88
+ if (identifierFormat != null) {
89
+ metadata.EntityDescriptor.SPSSODescriptor.NameIDFormat = identifierFormat;
90
+ }
91
+ if (wantAssertionsSigned) {
92
+ metadata.EntityDescriptor.SPSSODescriptor["@WantAssertionsSigned"] = true;
93
+ }
94
+ metadata.EntityDescriptor.SPSSODescriptor.AssertionConsumerService = {
95
+ "@index": "1",
96
+ "@isDefault": "true",
97
+ "@Binding": "urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST",
98
+ "@Location": callbackUrl,
99
+ };
100
+ let metadataXml = (0, xml_1.buildXmlBuilderObject)(metadata, true);
101
+ if (params.signMetadata === true && (0, types_1.isValidSamlSigningOptions)(params)) {
102
+ metadataXml = (0, utility_1.signXmlMetadata)(metadataXml, {
103
+ privateKey: params.privateKey,
104
+ signatureAlgorithm: params.signatureAlgorithm,
105
+ xmlSignatureTransforms: params.xmlSignatureTransforms,
106
+ digestAlgorithm: params.digestAlgorithm,
107
+ });
108
+ }
109
+ return metadataXml;
110
+ };
111
+ exports.generateServiceProviderMetadata = generateServiceProviderMetadata;
112
+ //# sourceMappingURL=metadata.js.map
@@ -0,0 +1,8 @@
1
+ import * as passport from "passport";
2
+ export interface AuthenticateOptions extends passport.AuthenticateOptions {
3
+ samlFallback?: "login-request" | "logout-request";
4
+ additionalParams?: Record<string, string | string[]>;
5
+ }
6
+ export interface AuthorizeOptions extends AuthenticateOptions {
7
+ samlFallback?: "login-request" | "logout-request";
8
+ }
@@ -0,0 +1,3 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ //# sourceMappingURL=passport-saml-types.js.map
@@ -0,0 +1,3 @@
1
+ import { SamlSigningOptions } from "./types";
2
+ export declare function signSamlPost(samlMessage: string, xpath: string, options: SamlSigningOptions): string;
3
+ export declare function signAuthnRequestPost(authnRequest: string, options: SamlSigningOptions): string;
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.signAuthnRequestPost = exports.signSamlPost = void 0;
4
+ const xml_1 = require("./xml");
5
+ const authnRequestXPath = '/*[local-name(.)="AuthnRequest" and namespace-uri(.)="urn:oasis:names:tc:SAML:2.0:protocol"]';
6
+ const issuerXPath = '/*[local-name(.)="Issuer" and namespace-uri(.)="urn:oasis:names:tc:SAML:2.0:assertion"]';
7
+ function signSamlPost(samlMessage, xpath, options) {
8
+ return (0, xml_1.signXml)(samlMessage, xpath, { reference: xpath + issuerXPath, action: "after" }, options);
9
+ }
10
+ exports.signSamlPost = signSamlPost;
11
+ function signAuthnRequestPost(authnRequest, options) {
12
+ return signSamlPost(authnRequest, authnRequestXPath, options);
13
+ }
14
+ exports.signAuthnRequestPost = signAuthnRequestPost;
15
+ //# sourceMappingURL=saml-post-signing.js.map
@@ -0,0 +1,75 @@
1
+ /// <reference types="node" />
2
+ /// <reference types="node" />
3
+ import * as crypto from "crypto";
4
+ import * as querystring from "querystring";
5
+ import { ParsedQs } from "qs";
6
+ import { AudienceRestrictionXML, CacheProvider, Profile, SamlOptions, SamlConfig, XMLOutput } from "./types";
7
+ import { AuthenticateOptions, AuthorizeOptions } from "./passport-saml-types";
8
+ declare class SAML {
9
+ /**
10
+ * Note that some methods in SAML are not yet marked as protected as they are used in testing.
11
+ * Those methods start with an underscore, e.g. _generateLogoutRequest
12
+ */
13
+ options: SamlOptions;
14
+ cacheProvider: CacheProvider;
15
+ constructor(ctorOptions: SamlConfig);
16
+ initialize(ctorOptions: SamlConfig): SamlOptions;
17
+ protected getCallbackUrl(host?: string | undefined): string;
18
+ protected signRequest(samlMessage: querystring.ParsedUrlQueryInput): void;
19
+ protected generateAuthorizeRequestAsync(this: SAML, isPassive: boolean, isHttpPostBinding: boolean, host: string | undefined): Promise<string>;
20
+ _generateLogoutRequest(this: SAML, user: Profile): Promise<string>;
21
+ _generateLogoutResponse(this: SAML, logoutRequest: Profile, success: boolean): string;
22
+ _requestToUrlAsync(request: string | null | undefined, response: string | null, operation: string, additionalParameters: querystring.ParsedUrlQuery): Promise<string>;
23
+ _getAdditionalParams(relayState: string, operation: "authorize" | "logout", overrideParams?: querystring.ParsedUrlQuery): querystring.ParsedUrlQuery;
24
+ getAuthorizeUrlAsync(RelayState: string, host: string | undefined, options: AuthorizeOptions): Promise<string>;
25
+ getAuthorizeFormAsync(RelayState: string, host?: string): Promise<string>;
26
+ getLogoutUrlAsync(user: Profile, RelayState: string, options: AuthenticateOptions & AuthorizeOptions): Promise<string>;
27
+ getLogoutResponseUrl(samlLogoutRequest: Profile, RelayState: string, options: AuthenticateOptions & AuthorizeOptions, success: boolean, callback: (err: Error | null, url?: string) => void): void;
28
+ getLogoutResponseUrlAsync(samlLogoutRequest: Profile, RelayState: string, options: AuthenticateOptions & AuthorizeOptions, success: boolean): Promise<string>;
29
+ protected certsToCheck(): Promise<string[]>;
30
+ validatePostResponseAsync(container: Record<string, string>): Promise<{
31
+ profile: Profile | null;
32
+ loggedOut: boolean;
33
+ }>;
34
+ protected validateInResponseTo(inResponseTo: string | null): Promise<void>;
35
+ validateRedirectAsync(container: ParsedQs, originalQuery: string): Promise<{
36
+ profile: Profile | null;
37
+ loggedOut: boolean;
38
+ }>;
39
+ protected hasValidSignatureForRedirect(container: ParsedQs, originalQuery: string): Promise<boolean | void>;
40
+ protected validateSignatureForRedirect(urlString: crypto.BinaryLike, signature: string, alg: string, cert: string): boolean;
41
+ protected verifyLogoutRequest(doc: XMLOutput): void;
42
+ protected verifyLogoutResponse(doc: XMLOutput): Promise<void>;
43
+ protected verifyIssuer(samlMessage: XMLOutput): void;
44
+ protected processValidlySignedAssertionAsync(this: SAML, xml: string, samlResponseXml: string, inResponseTo: string | null): Promise<{
45
+ profile: Profile;
46
+ loggedOut: boolean;
47
+ }>;
48
+ protected checkTimestampsValidityError(nowMs: number, notBefore: string, notOnOrAfter: string, maxTimeLimitMs?: number): Error | null;
49
+ protected checkAudienceValidityError(expectedAudience: string, audienceRestrictions: AudienceRestrictionXML[]): Error | null;
50
+ validatePostRequestAsync(container: Record<string, string>): Promise<{
51
+ profile: Profile;
52
+ loggedOut: boolean;
53
+ }>;
54
+ protected processValidlySignedPostRequestAsync(this: SAML, doc: XMLOutput, dom: Document): Promise<{
55
+ profile: Profile;
56
+ loggedOut: boolean;
57
+ }>;
58
+ protected processValidlySignedSamlLogoutAsync(this: SAML, doc: XMLOutput, dom: Document): Promise<{
59
+ profile: Profile | null;
60
+ loggedOut: boolean;
61
+ }>;
62
+ generateServiceProviderMetadata(this: SAML, decryptionCert: string | null, signingCerts?: string | string[] | null): string;
63
+ /**
64
+ * Process max age assertion and use it if it is more restrictive than the NotOnOrAfter age
65
+ * assertion received in the SAMLResponse.
66
+ *
67
+ * @param maxAssertionAgeMs Max time after IssueInstant that we will accept assertion, in Ms.
68
+ * @param notOnOrAfter Expiration provided in response.
69
+ * @param issueInstant Time when response was issued.
70
+ * @returns {*} The expiration time to be used, in Ms.
71
+ */
72
+ protected calcMaxAgeAssertionTime(maxAssertionAgeMs: number, notOnOrAfter: string, issueInstant: string): number;
73
+ protected mustValidateInResponseTo(hasInResponseTo: boolean): boolean;
74
+ }
75
+ export { SAML };