@plusscommunities/pluss-core-aws 2.0.24-auth.0 → 2.0.24

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.
@@ -1,90 +1,75 @@
1
- // const https = require("https");
2
- // const jose = require("node-jose");
3
- // const { app_client_id, keys_url } = require("../../../config");
4
- // const isUserDisabled = require("./isUserDisabled");
5
- const { log } = require("..");
6
- const AuthenticationContext = require("./context/AuthenticationContext");
1
+ const https = require("https");
2
+ const jose = require("node-jose");
3
+ const { getConfig } = require("../../config");
4
+ const checkTokenBlacklist = require("./checkTokenBlacklist");
7
5
 
8
6
  module.exports = async (token) => {
9
- const logId = log("getSessionUser", "Start", true);
10
- const userId = await AuthenticationContext.getSessionUser(token);
11
- log("getSessionUser", "Result", userId, logId);
12
- return userId;
13
- // return new Promise((resolve, reject) => {
14
- // if (!token) {
15
- // return resolve(null);
16
- // }
17
- // var sections = token.split(".");
18
- // // get the kid from the headers prior to verification
19
- // var header = jose.util.base64url.decode(sections[0]);
20
- // header = JSON.parse(header);
21
- // var kid = header.kid;
22
- // // download the public keys
23
- // https.get(keys_url, async (response) => {
24
- // if (response.statusCode == 200) {
25
- // response.on("data", async (body) => {
26
- // var keys = JSON.parse(body)["keys"];
27
- // // search for the kid in the downloaded public keys
28
- // var key_index = -1;
29
- // for (var i = 0; i < keys.length; i++) {
30
- // if (kid == keys[i].kid) {
31
- // key_index = i;
32
- // break;
33
- // }
34
- // }
35
- // if (key_index == -1) {
36
- // reject();
37
- // return;
38
- // }
39
- // // construct the public key
40
- // jose.JWK.asKey(keys[key_index])
41
- // .then(async (result) => {
42
- // // verify the signature
43
- // jose.JWS.createVerify(result)
44
- // .verify(token)
45
- // .then(async (result2) => {
46
- // // now we can use the claims
47
- // var claims = JSON.parse(result2.payload);
48
- // // additionally we can verify the token expiration
49
- // var current_ts = Math.floor(new Date() / 1000);
50
- // if (current_ts > claims.exp) {
51
- // console.log("Token is expired");
52
- // reject("Token is expired");
53
- // return;
54
- // }
7
+ return new Promise(async (resolve, reject) => {
8
+ if (!token) {
9
+ return resolve(null);
10
+ }
55
11
 
56
- // /* --=- Optional audience stuff we dont use.
57
- // // and the Audience (use claims.client_id if verifying an access token)
58
- // if (claims.aud != app_client_id) {
59
- // console.log('Token was not issued for this audience')
60
- // return;
61
- // }
62
- // */
12
+ // Check if token is blacklisted before expensive verification
13
+ const isBlacklisted = await checkTokenBlacklist(token);
14
+ if (isBlacklisted) {
15
+ reject("Token has been invalidated");
16
+ return;
17
+ }
63
18
 
64
- // const isDisabled = await isUserDisabled(claims.username);
65
-
66
- // if (isDisabled) {
67
- // console.log("User is disabled");
68
- // reject("User is disabled");
69
- // return;
70
- // }
71
-
72
- // resolve(claims.username);
73
- // })
74
- // .catch(async (error) => {
75
- // console.log("Signature verification failed", error);
76
- // reject("Signature verification failed");
77
- // });
78
- // })
79
- // .catch(async (error) => {
80
- // console.log("failed JWK.asKey", error);
81
- // reject(error);
82
- // });
83
- // });
84
- // } else {
85
- // console.log("failed on response", response);
86
- // reject(response);
87
- // }
88
- // });
89
- // });
19
+ var sections = token.split(".");
20
+ // get the kid from the headers prior to verification
21
+ var header = jose.util.base64url.decode(sections[0]);
22
+ header = JSON.parse(header);
23
+ var kid = header.kid;
24
+ // download the public keys
25
+ https.get(getConfig().keys_url, function (response) {
26
+ if (response.statusCode == 200) {
27
+ response.on("data", function (body) {
28
+ var keys = JSON.parse(body)["keys"];
29
+ // search for the kid in the downloaded public keys
30
+ var key_index = -1;
31
+ for (var i = 0; i < keys.length; i++) {
32
+ if (kid == keys[i].kid) {
33
+ key_index = i;
34
+ break;
35
+ }
36
+ }
37
+ if (key_index == -1) {
38
+ reject();
39
+ return;
40
+ }
41
+ // construct the public key
42
+ jose.JWK.asKey(keys[key_index])
43
+ .then(function (result) {
44
+ // verify the signature
45
+ jose.JWS.createVerify(result)
46
+ .verify(token)
47
+ .then(function (result2) {
48
+ // now we can use the claims
49
+ var claims = JSON.parse(result2.payload);
50
+ // additionally we can verify the token expiration
51
+ var current_ts = Math.floor(new Date() / 1000);
52
+ if (current_ts > claims.exp) {
53
+ console.log("Token is expired");
54
+ reject("Token is expired");
55
+ return;
56
+ }
57
+ resolve(claims.username);
58
+ })
59
+ .catch(function (error) {
60
+ console.log("Signature verification failed", error);
61
+ reject("Signature verification failed");
62
+ });
63
+ })
64
+ .catch(function (error) {
65
+ console.log("failed JWK.asKey", error);
66
+ reject(error);
67
+ });
68
+ });
69
+ } else {
70
+ console.log("failed on response", response);
71
+ reject(response);
72
+ }
73
+ });
74
+ });
90
75
  };
@@ -0,0 +1,117 @@
1
+ /**
2
+ * Notify SiteConfigs Service of Operational Data Changes
3
+ *
4
+ * This helper provides a pre-filtering mechanism for auto-propagation.
5
+ * When operational data changes (sites, usertypes, interfaces, strings, jobTypes),
6
+ * this function:
7
+ *
8
+ * 1. Queries SourceSiteIdIndex to check if any templates use this site as source
9
+ * 2. If none found → early return (no Lambda invoke, 99% of cases)
10
+ * 3. If templates found → invokes siteConfigs Lambda to handle republishing
11
+ *
12
+ * This hybrid approach ensures:
13
+ * - No Lambda latency for most sites (pre-filter catches non-source sites)
14
+ * - Publishing logic stays encapsulated in siteConfigs service
15
+ * - Minimal cross-service coupling
16
+ *
17
+ * Usage:
18
+ * const notifySiteConfigs = require("@plusscommunities/pluss-core-aws/helper/notifySiteConfigs");
19
+ * await notifySiteConfigs(siteId, logId);
20
+ */
21
+
22
+ const AWS = require("aws-sdk");
23
+ const indexQuery = require("../db/common/indexQuery");
24
+ const { log } = require("./index");
25
+
26
+ const lambda = new AWS.Lambda();
27
+
28
+ const TABLE_NAME = "siteconfigs";
29
+
30
+ /**
31
+ * Check if a site has any templates using it as a source
32
+ * @param {string} siteId - The site ID to check
33
+ * @returns {Promise<Array>} Array of template IDs (empty if none)
34
+ */
35
+ const getTemplatesForSourceSite = async (siteId) => {
36
+ try {
37
+ const result = await indexQuery(TABLE_NAME, {
38
+ IndexName: "SourceSiteIdIndex",
39
+ KeyConditionExpression: "SourceSiteId = :sourceSiteId",
40
+ ExpressionAttributeValues: {
41
+ ":sourceSiteId": siteId,
42
+ },
43
+ ProjectionExpression: "Id", // Only need IDs for pre-filter
44
+ });
45
+ return (result.Items || []).map((item) => item.Id);
46
+ } catch (err) {
47
+ // Table or index might not exist in all deployments
48
+ log("notifySiteConfigs", "QueryError", { siteId, error: err.message });
49
+ return [];
50
+ }
51
+ };
52
+
53
+ /**
54
+ * Invoke the siteConfigs operationalDataChanged Lambda
55
+ * @param {string} siteId - The source site that changed
56
+ * @param {Array<string>} templateIds - List of template IDs to republish
57
+ * @param {string} logId - Log ID for tracing
58
+ */
59
+ const invokeSiteConfigsLambda = async (siteId, templateIds, logId) => {
60
+ // Build function name from environment
61
+ // Format: {client}-siteConfigs-{stage}-operationalDataChanged
62
+ const client = process.env.client || "dev";
63
+ const stage = process.env.stage || "dev";
64
+ const functionName = `${client}-siteConfigs-${stage}-operationalDataChanged`;
65
+
66
+ const payload = {
67
+ siteId,
68
+ templateIds,
69
+ logId,
70
+ source: "notifySiteConfigs",
71
+ };
72
+
73
+ log("notifySiteConfigs", "InvokingLambda", { functionName, siteId, templateCount: templateIds.length }, logId);
74
+
75
+ try {
76
+ await lambda
77
+ .invoke({
78
+ FunctionName: functionName,
79
+ InvocationType: "Event", // Async invocation
80
+ Payload: JSON.stringify(payload),
81
+ })
82
+ .promise();
83
+
84
+ log("notifySiteConfigs", "LambdaInvoked", { functionName, siteId }, logId);
85
+ } catch (err) {
86
+ log("notifySiteConfigs", "LambdaError", { functionName, siteId, error: err.message }, logId);
87
+ // Don't throw - this is a non-critical operation
88
+ }
89
+ };
90
+
91
+ /**
92
+ * Notify siteConfigs service that operational data has changed for a site.
93
+ * Performs pre-filtering to avoid unnecessary Lambda invocations.
94
+ *
95
+ * @param {string} siteId - The site ID where operational data changed
96
+ * @param {string} logId - Optional log ID for tracing
97
+ */
98
+ const notifySiteConfigs = async (siteId, logId) => {
99
+ if (!siteId) {
100
+ return;
101
+ }
102
+
103
+ // Pre-filter: Check if this site is a source for any templates
104
+ const templateIds = await getTemplatesForSourceSite(siteId);
105
+
106
+ if (templateIds.length === 0) {
107
+ // No Lambda invocation needed
108
+ return;
109
+ }
110
+
111
+ log("notifySiteConfigs", "TemplatesFound", { siteId, count: templateIds.length }, logId);
112
+
113
+ // Invoke siteConfigs Lambda to handle republishing
114
+ await invokeSiteConfigsLambda(siteId, templateIds, logId);
115
+ };
116
+
117
+ module.exports = notifySiteConfigs;
package/package.json CHANGED
@@ -1,15 +1,12 @@
1
1
  {
2
2
  "name": "@plusscommunities/pluss-core-aws",
3
- "version": "2.0.24-auth.0",
3
+ "version": "2.0.24",
4
4
  "description": "Core extension package for Pluss Communities platform",
5
5
  "scripts": {
6
6
  "betapatch": "npm version prepatch --preid=beta",
7
7
  "patch": "npm version patch",
8
8
  "betaupload": "npm i && npm i && npm publish --access public --tag beta",
9
9
  "betaupload:p": "npm run betapatch && npm run betaupload",
10
- "authpatch": "npm version prepatch --preid=auth",
11
- "authupload": "npm i && npm i && npm publish --access public --tag auth",
12
- "authupload:p": "npm run authpatch && npm run authupload",
13
10
  "upload": "npm i && npm i && npm publish --access public",
14
11
  "upload:p": "npm run patch && npm run upload"
15
12
  },
@@ -23,8 +20,6 @@
23
20
  "expo-server-sdk": "^3.0.1",
24
21
  "html-entities": "^2.3.2",
25
22
  "https": "^1.0.0",
26
- "jsonwebtoken": "^9.0.2",
27
- "jwks-rsa": "^3.1.0",
28
23
  "lodash": "^4.17.10",
29
24
  "moment": "^2.30.1",
30
25
  "moment-timezone": "^0.5.41",
@@ -1,50 +0,0 @@
1
- const Auth0Strategy = require("./auth0/Strategy");
2
- const CognitoStrategy = require("./cognito/Strategy");
3
- const BoltonClarkeStrategy = require("./boltonclarke/Strategy");
4
- const { getConfig } = require("../../../config");
5
-
6
- class AuthenticationContext {
7
- static strategy = null;
8
-
9
- static initialiseStrategy() {
10
- switch (getConfig().authConfig.provider) {
11
- case "auth0":
12
- AuthenticationContext.strategy = new Auth0Strategy();
13
- break;
14
- case "boltonclarke":
15
- AuthenticationContext.strategy = new BoltonClarkeStrategy();
16
- break;
17
- case "cognito":
18
- AuthenticationContext.strategy = new CognitoStrategy();
19
- break;
20
- default:
21
- throw new Error("Invalid authentication provider specified");
22
- }
23
- }
24
-
25
- static getSessionUser = async (token) => {
26
- if (!AuthenticationContext.strategy) {
27
- AuthenticationContext.initialiseStrategy();
28
- }
29
- return AuthenticationContext.strategy.getSessionUser(token);
30
- };
31
-
32
- static populateUser = async (token) => {
33
- if (!AuthenticationContext.strategy) {
34
- AuthenticationContext.initialiseStrategy();
35
- }
36
- return AuthenticationContext.strategy.populateUser(token);
37
- };
38
-
39
- static updateIdentityAttributes = async (input, userId) => {
40
- if (!AuthenticationContext.strategy) {
41
- AuthenticationContext.initialiseStrategy();
42
- }
43
- return AuthenticationContext.strategy.updateIdentityAttributes(
44
- input,
45
- userId
46
- );
47
- };
48
- }
49
-
50
- module.exports = AuthenticationContext;
@@ -1,20 +0,0 @@
1
- // Authentication Strategy Interface
2
- class AuthenticationStrategy {
3
- constructor() {}
4
-
5
- getSessionUser(token) {
6
- throw new Error("Method 'getSessionUser()' must be implemented.");
7
- }
8
-
9
- // optional function to populate user data based on a token
10
- populateUser(token) {
11
- return;
12
- }
13
-
14
- // optional function to save attributes to identity provider
15
- updateIdentityAttributes = async (input, userId) => {
16
- return;
17
- };
18
- }
19
-
20
- module.exports = AuthenticationStrategy;
@@ -1,12 +0,0 @@
1
- // auth0Strategy.js
2
- const AuthenticationStrategy = require("../AuthenticationStrategy");
3
- const getSessionUser = require("./functions/getSessionUser");
4
-
5
- class Auth0Strategy extends AuthenticationStrategy {
6
- constructor() {
7
- super();
8
- this.getSessionUser = getSessionUser;
9
- }
10
- }
11
-
12
- module.exports = Auth0Strategy;
@@ -1,45 +0,0 @@
1
- const jwt = require("jsonwebtoken");
2
- const jwksClient = require("jwks-rsa");
3
-
4
- const { getConfig } = require("../../../../../config");
5
-
6
- // Function to retrieve the signing key from Auth0 JWKS
7
- const getKey = (header, callback) => {
8
- // Initialize JWKS client
9
- const client = jwksClient({
10
- jwksUri: `https://${getConfig().auth0Config.domain}/.well-known/jwks.json`,
11
- });
12
-
13
- client.getSigningKey(header.kid, (err, key) => {
14
- if (err) {
15
- callback(err, null);
16
- } else {
17
- const signingKey = key.publicKey || key.rsaPublicKey;
18
- callback(null, signingKey);
19
- }
20
- });
21
- };
22
-
23
- // Function to validate the token and extract user information
24
- const decodeAccessToken = async (token) => {
25
- return new Promise((resolve, reject) => {
26
- jwt.verify(
27
- token,
28
- getKey,
29
- {
30
- audience: getConfig().auth0Config.audience,
31
- issuer: `https://${getConfig().auth0Config.domain}/`,
32
- algorithms: ["RS256"],
33
- },
34
- (err, decoded) => {
35
- if (err) {
36
- reject(err);
37
- } else {
38
- resolve(decoded); // 'sub' contains the user ID
39
- }
40
- }
41
- );
42
- });
43
- };
44
-
45
- module.exports = decodeAccessToken;
@@ -1,21 +0,0 @@
1
- const decodeAccessToken = require("./decodeAccessToken");
2
- const { getConfig } = require("../../../../../config");
3
-
4
- // Function to validate the token and extract user information
5
- const getSessionUser = async (token) => {
6
- return new Promise((resolve, reject) => {
7
- decodeAccessToken(token)
8
- .then((claims) => {
9
- return resolve(
10
- claims[getConfig().auth0Config.residentIdClaim] ||
11
- claims[getConfig().auth0Config.staffIdClaim] ||
12
- claims[getConfig().auth0Config.userIdClaim]
13
- );
14
- })
15
- .catch((err) => {
16
- reject(err);
17
- });
18
- });
19
- };
20
-
21
- module.exports = getSessionUser;
@@ -1,10 +0,0 @@
1
- // auth0Strategy.js
2
- const Auth0Strategy = require("../auth0/Strategy");
3
-
4
- class BoltonClarkeStrategy extends Auth0Strategy {
5
- constructor() {
6
- super();
7
- }
8
- }
9
-
10
- module.exports = BoltonClarkeStrategy;
@@ -1,12 +0,0 @@
1
- // cognitoStrategy.js
2
- const AuthenticationStrategy = require("../AuthenticationStrategy");
3
- const getSessionUser = require("./functions/getSessionUser");
4
-
5
- class CognitoStrategy extends AuthenticationStrategy {
6
- constructor() {
7
- super();
8
- this.getSessionUser = getSessionUser;
9
- }
10
- }
11
-
12
- module.exports = CognitoStrategy;
@@ -1,76 +0,0 @@
1
- const https = require("https");
2
- const jose = require("node-jose");
3
-
4
- const { getConfig } = require("../../../../../config");
5
-
6
- module.exports = async (token) => {
7
- return new Promise((resolve, reject) => {
8
- if (!token) {
9
- return resolve(null);
10
- }
11
- var sections = token.split(".");
12
- // get the kid from the headers prior to verification
13
- var header = jose.util.base64url.decode(sections[0]);
14
- header = JSON.parse(header);
15
- var kid = header.kid;
16
- // download the public keys
17
- https.get(getConfig().keys_url, async (response) => {
18
- if (response.statusCode == 200) {
19
- response.on("data", async (body) => {
20
- var keys = JSON.parse(body)["keys"];
21
- // search for the kid in the downloaded public keys
22
- var key_index = -1;
23
- for (var i = 0; i < keys.length; i++) {
24
- if (kid == keys[i].kid) {
25
- key_index = i;
26
- break;
27
- }
28
- }
29
- if (key_index == -1) {
30
- reject();
31
- return;
32
- }
33
- // construct the public key
34
- jose.JWK.asKey(keys[key_index])
35
- .then(async (result) => {
36
- // verify the signature
37
- jose.JWS.createVerify(result)
38
- .verify(token)
39
- .then(async (result2) => {
40
- // now we can use the claims
41
- var claims = JSON.parse(result2.payload);
42
- // additionally we can verify the token expiration
43
- var current_ts = Math.floor(new Date() / 1000);
44
- if (current_ts > claims.exp) {
45
- console.log("Token is expired");
46
- reject("Token is expired");
47
- return;
48
- }
49
-
50
- // const isDisabled = await isUserDisabled(claims.username);
51
-
52
- // if (isDisabled) {
53
- // console.log("User is disabled");
54
- // reject("User is disabled");
55
- // return;
56
- // }
57
-
58
- resolve(claims.username);
59
- })
60
- .catch(async (error) => {
61
- console.log("Signature verification failed", error);
62
- reject("Signature verification failed");
63
- });
64
- })
65
- .catch(async (error) => {
66
- console.log("failed JWK.asKey", error);
67
- reject(error);
68
- });
69
- });
70
- } else {
71
- console.log("failed on response", response);
72
- reject(response);
73
- }
74
- });
75
- });
76
- };