@plusscommunities/pluss-core-aws 2.0.25-auth.0 → 2.0.25-beta.1
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/aws/getDefaultEmailAddress.js +21 -21
- package/aws/getEmailService.js +16 -16
- package/aws/getEmailServiceInfo.js +26 -26
- package/aws/sendEmail.js +31 -31
- package/config.js +1 -1
- package/db/activity/publishActivity.js +22 -22
- package/db/analytics/checkActivityExists.js +15 -15
- package/db/analytics/logAnalyticsActivity.js +69 -37
- package/db/analytics/scheduleOldAggregation.js +14 -14
- package/db/auth/getSiteSetting.js +12 -12
- package/db/auth/getSiteUserTypes.js +16 -16
- package/db/auth/getUserAuth.js +13 -13
- package/db/automatedactions/getActionBySiteTrigger.js +9 -9
- package/db/common/deleteRef.js +21 -21
- package/db/common/editRef.js +36 -36
- package/db/common/getRef.js +23 -23
- package/db/common/getTableCount.js +18 -18
- package/db/common/indexQuery.js +17 -17
- package/db/common/indexQueryRecursive.js +20 -20
- package/db/common/scanRef.js +18 -18
- package/db/common/scanRefRecursive.js +20 -20
- package/db/common/updateAttribute.js +27 -27
- package/db/common/updateRef.js +20 -20
- package/db/linkedUsers/getLinkedBy.js +21 -21
- package/db/linkedUsers/getLinkedTo.js +21 -21
- package/db/notifications/deleteNotificationsByEntity.js +21 -21
- package/db/notifications/getNotificationSetting.js +14 -14
- package/db/notifications/publishNotifications.js +39 -39
- package/db/scheduledActions/deleteActionQueue.js +1 -1
- package/db/scheduledActions/getActionQueueByEntityId.js +10 -10
- package/db/scheduledActions/getActionQueueByEntityKey.js +9 -9
- package/db/scheduledActions/getActionQueueById.js +9 -9
- package/db/scheduledActions/getActionQueueByTriggerAt.js +14 -14
- package/db/scheduledActions/updateActionQueue.js +29 -29
- package/db/strings/getString.js +20 -20
- package/db/strings/logUpdate.js +18 -18
- package/db/templates/getTemplateById.js +1 -1
- package/db/templates/getTemplatesList.js +10 -10
- package/db/templates/updateTemplate.js +9 -9
- package/db/users/getRole.js +1 -1
- package/db/users/getUser.js +9 -9
- package/db/users/getUserByEmail.js +17 -17
- package/helper/audience/filterByAudienceType.js +27 -27
- package/helper/audience/filterOnAudienceType.js +26 -26
- package/helper/audience/getAudience.js +187 -187
- package/helper/audience/getMatchingAudienceTypes.js +21 -21
- package/helper/audience/getMatchingAudienceTypesFromPreview.js +60 -60
- package/helper/audience/getMatchingTags.js +15 -15
- package/helper/audience/isValidAudience.js +20 -20
- package/helper/auth/checkTokenBlacklist.js +17 -17
- package/helper/auth/getApiKeyFromReq.js +2 -2
- package/helper/auth/getSessionUser.js +70 -85
- package/helper/auth/getSessionUserFromReq.js +2 -2
- package/helper/auth/getSessionUserFromReqAuthKey.js +11 -11
- package/helper/auth/validateApiKey.js +32 -32
- package/helper/auth/validateMasterAuth.js +174 -174
- package/helper/auth/validateSiteAccess.js +12 -12
- package/helper/auth/validateSiteSetting.js +7 -7
- package/helper/auth/validateUserLoggedIn.js +19 -19
- package/helper/createGuid.js +5 -5
- package/helper/generateJsonResponse.js +27 -27
- package/helper/getUserPreview.js +57 -57
- package/helper/getUserPreviewFromHeader.js +17 -17
- package/helper/getUserPreviewFromReq.js +17 -17
- package/helper/hqPublishing.js +337 -0
- package/helper/index.js +28 -28
- package/helper/notifySiteConfigs.js +132 -0
- package/helper/opengraph/getOpenGraph.js +12 -12
- package/helper/rates/checkRateLimit.js +38 -38
- package/helper/requestToSource.js +10 -10
- package/helper/sendEmail.js +120 -120
- package/helper/templates/replacePlaceHolders.js +29 -29
- package/helper/time/getLocalTimestamp.js +18 -18
- package/helper/time/getSiteTimezone.js +11 -11
- package/helper/triggerAutomatedAction.js +25 -25
- package/helper/userToUserPreview.js +23 -23
- package/helper/users/getUserTypesByPermission.js +24 -24
- package/helper/users/getUsersByPermission.js +20 -20
- package/notification/prepNotification.js +144 -144
- package/notification/sendNotifications.js +166 -166
- package/package.json +35 -40
- package/templates/supportTicketEmails.js +8 -8
- package/helper/auth/context/AuthenticationContext.js +0 -50
- package/helper/auth/context/AuthenticationStrategy.js +0 -20
- package/helper/auth/context/auth0/Strategy.js +0 -12
- package/helper/auth/context/auth0/functions/decodeAccessToken.js +0 -102
- package/helper/auth/context/auth0/functions/getSessionUser.js +0 -21
- package/helper/auth/context/boltonclarke/Strategy.js +0 -10
- package/helper/auth/context/cognito/Strategy.js +0 -12
- package/helper/auth/context/cognito/functions/getSessionUser.js +0 -76
|
@@ -9,186 +9,186 @@ const { getMultiRowId } = require("../helper");
|
|
|
9
9
|
const { log } = require("../helper");
|
|
10
10
|
|
|
11
11
|
const SendNotification = async (tokens, message, type, key, params, config) => {
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
const logId = log("SendNotification", "input", {
|
|
13
|
+
tokens,
|
|
14
|
+
message,
|
|
15
|
+
type,
|
|
16
|
+
key,
|
|
17
|
+
params,
|
|
18
|
+
config,
|
|
19
|
+
});
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
21
|
+
const expo = new Expo();
|
|
22
|
+
const messages = [];
|
|
23
|
+
const data = {
|
|
24
|
+
type,
|
|
25
|
+
key,
|
|
26
|
+
};
|
|
27
|
+
Object.keys(params).forEach((paramKey) => {
|
|
28
|
+
data[paramKey] = params[paramKey];
|
|
29
|
+
});
|
|
30
|
+
data.muteConfig = config;
|
|
31
|
+
log("SendNotification", "params", data, logId);
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
33
|
+
for (const token of tokens) {
|
|
34
|
+
if (Expo.isExpoPushToken(token)) {
|
|
35
|
+
const msg = {
|
|
36
|
+
to: token,
|
|
37
|
+
sound: "default",
|
|
38
|
+
body: message,
|
|
39
|
+
data,
|
|
40
|
+
badge: 1,
|
|
41
|
+
};
|
|
42
|
+
if (data.muteConfig && !data.muteConfig.ignoreMute) {
|
|
43
|
+
msg.categoryId = data.muteConfig.type
|
|
44
|
+
? `mute_${data.muteConfig.type === "app" ? "app" : "entity"}`
|
|
45
|
+
: "mute_app";
|
|
46
|
+
}
|
|
47
|
+
messages.push(msg);
|
|
48
|
+
} else {
|
|
49
|
+
const message = `Push token ${token} is not a valid Expo push token`;
|
|
50
|
+
log("SendNotification", "IsTokenError", message, logId);
|
|
51
|
+
if (config.raiseError) throw new Error(message);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
54
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
55
|
+
log("SendNotification", "messages", messages);
|
|
56
|
+
const chunks = expo.chunkPushNotifications(messages);
|
|
57
|
+
for (const chunk of chunks) {
|
|
58
|
+
try {
|
|
59
|
+
const receipts = await expo.sendPushNotificationsAsync(chunk);
|
|
60
|
+
log("SendNotification", "Receipts", receipts, logId);
|
|
61
|
+
} catch (error) {
|
|
62
|
+
if (error.code && error.code === "PUSH_TOO_MANY_EXPERIENCE_IDS") {
|
|
63
|
+
log(
|
|
64
|
+
"SendNotification",
|
|
65
|
+
"CaughtError",
|
|
66
|
+
"PUSH_TOO_MANY_EXPERIENCE_IDS",
|
|
67
|
+
logId,
|
|
68
|
+
);
|
|
69
|
+
// send separately
|
|
70
|
+
await Promise.all(
|
|
71
|
+
_.values(error.details).map((appTokens) =>
|
|
72
|
+
SendNotification(appTokens, message, type, key, params, config),
|
|
73
|
+
),
|
|
74
|
+
);
|
|
75
|
+
} else {
|
|
76
|
+
log("SendNotification", "SendError", error, logId);
|
|
77
|
+
if (config.raiseError) throw error;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
81
|
};
|
|
82
82
|
|
|
83
83
|
const checkMuted = async (userId, config) => {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
if (!config.ignoreMute) {
|
|
85
|
+
const notiSettings = await getNotificationSetting(userId);
|
|
86
|
+
const logId = log("checkMuted", "notiSettings", notiSettings);
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
88
|
+
const appSetting = notiSettings.find((n) => n.EntityType === "app");
|
|
89
|
+
const isAppMuted = appSetting
|
|
90
|
+
? moment() <= moment(appSetting.Expiry)
|
|
91
|
+
: false;
|
|
92
|
+
log("checkMuted", "isAppMuted", isAppMuted, logId);
|
|
93
|
+
if (isAppMuted) return true;
|
|
94
94
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
95
|
+
const entitySetting = notiSettings.find(
|
|
96
|
+
(n) => n.EntityType === config.type && n.EntityId === config.id,
|
|
97
|
+
);
|
|
98
|
+
const isEntityMuted = entitySetting
|
|
99
|
+
? moment() <= moment(entitySetting.Expiry)
|
|
100
|
+
: false;
|
|
101
|
+
log("checkMuted", "isEntityMuted", isEntityMuted, logId);
|
|
102
|
+
if (isEntityMuted) return true;
|
|
103
|
+
}
|
|
104
104
|
|
|
105
|
-
|
|
105
|
+
return false;
|
|
106
106
|
};
|
|
107
107
|
|
|
108
108
|
module.exports = async (
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
109
|
+
receiverKeys,
|
|
110
|
+
message,
|
|
111
|
+
type,
|
|
112
|
+
key,
|
|
113
|
+
value,
|
|
114
|
+
config = { type: "app", id: null, ignoreMute: false, raiseError: false },
|
|
115
115
|
) => {
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
116
|
+
const logId = log("sendNotifications", "input", {
|
|
117
|
+
receiverKeys,
|
|
118
|
+
message,
|
|
119
|
+
type,
|
|
120
|
+
key,
|
|
121
|
+
value,
|
|
122
|
+
config,
|
|
123
|
+
});
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
125
|
+
// Generate content hash from the entire value object for per-user deduplication
|
|
126
|
+
const contentHash = crypto
|
|
127
|
+
.createHash("md5")
|
|
128
|
+
.update(JSON.stringify(value))
|
|
129
|
+
.digest("hex")
|
|
130
|
+
.substring(0, 8); // 8 hex chars for content uniqueness
|
|
131
131
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
132
|
+
const tokens = [];
|
|
133
|
+
const promises = [];
|
|
134
|
+
receiverKeys.forEach((uid) => {
|
|
135
|
+
promises.push(
|
|
136
|
+
new Promise((resolve, reject) => {
|
|
137
|
+
getUser(uid)
|
|
138
|
+
.then(async (user) => {
|
|
139
|
+
try {
|
|
140
|
+
// User-level rate limiting: check if this specific user can receive this notification
|
|
141
|
+
// Use user ID + content hash + notification type/key for fine-grained deduplication
|
|
142
|
+
const userNotificationKey = getMultiRowId([
|
|
143
|
+
"notification",
|
|
144
|
+
user.Id, // User-specific identifier
|
|
145
|
+
contentHash, // Content hash for this notification
|
|
146
|
+
type,
|
|
147
|
+
key,
|
|
148
|
+
]);
|
|
149
|
+
const canSend = await checkRateLimit(
|
|
150
|
+
userNotificationKey,
|
|
151
|
+
86400000, // 24 hours in milliseconds
|
|
152
|
+
1, // Allow only 1 notification in the timeframe
|
|
153
|
+
false, // Save this check to prevent duplicates
|
|
154
|
+
);
|
|
155
155
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
156
|
+
const isMuted = await checkMuted(user.Id, config);
|
|
157
|
+
if (!isMuted && canSend) {
|
|
158
|
+
if (!user.tokens) {
|
|
159
|
+
if (user.token) {
|
|
160
|
+
tokens.push(user.token);
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
_.values(user.tokens).forEach((entry) => {
|
|
164
|
+
tokens.push(entry.Token);
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
} else {
|
|
168
|
+
log(
|
|
169
|
+
"sendNotifications",
|
|
170
|
+
"userSkipped",
|
|
171
|
+
{
|
|
172
|
+
userId: user.Id,
|
|
173
|
+
isMuted,
|
|
174
|
+
canSend,
|
|
175
|
+
userNotificationKey,
|
|
176
|
+
},
|
|
177
|
+
logId,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
} catch (error) {
|
|
181
|
+
log("sendNotifications", "error", error.toString());
|
|
182
|
+
if (config.raiseError) reject(error);
|
|
183
|
+
}
|
|
184
|
+
resolve();
|
|
185
|
+
})
|
|
186
|
+
.catch((err) => {
|
|
187
|
+
resolve();
|
|
188
|
+
});
|
|
189
|
+
}),
|
|
190
|
+
);
|
|
191
|
+
});
|
|
192
|
+
await Promise.all(promises);
|
|
193
|
+
await SendNotification(tokens, message, type, key, value, config);
|
|
194
194
|
};
|
package/package.json
CHANGED
|
@@ -1,42 +1,37 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
"devDependencies": {
|
|
38
|
-
"eslint-config-rallycoding": "^3.2.0",
|
|
39
|
-
"serverless-domain-manager": "^3.3.1",
|
|
40
|
-
"serverless-prune-plugin": "^1.4.1"
|
|
41
|
-
}
|
|
2
|
+
"name": "@plusscommunities/pluss-core-aws",
|
|
3
|
+
"version": "2.0.25-beta.1",
|
|
4
|
+
"description": "Core extension package for Pluss Communities platform",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"betapatch": "npm version prepatch --preid=beta",
|
|
7
|
+
"patch": "npm version patch",
|
|
8
|
+
"betaupload": "npm i && npm i && npm publish --access public --tag beta",
|
|
9
|
+
"betaupload:p": "npm run betapatch && npm run betaupload",
|
|
10
|
+
"upload": "npm i && npm i && npm publish --access public",
|
|
11
|
+
"upload:p": "npm run patch && npm run upload"
|
|
12
|
+
},
|
|
13
|
+
"author": "Thorbjorn Kappel Davis",
|
|
14
|
+
"license": "ISC",
|
|
15
|
+
"dependencies": {
|
|
16
|
+
"@aws/dynamodb-auto-marshaller": "^0.7.1",
|
|
17
|
+
"amazon-cognito-identity-js": "^2.0.19",
|
|
18
|
+
"axios": "^1.6.8",
|
|
19
|
+
"aws-sdk": "^2.1591.0",
|
|
20
|
+
"expo-server-sdk": "^3.0.1",
|
|
21
|
+
"html-entities": "^2.3.2",
|
|
22
|
+
"https": "^1.0.0",
|
|
23
|
+
"lodash": "^4.17.10",
|
|
24
|
+
"moment": "^2.30.1",
|
|
25
|
+
"moment-timezone": "^0.5.41",
|
|
26
|
+
"node-fetch": "^2.2.0",
|
|
27
|
+
"node-jose": "^1.0.0",
|
|
28
|
+
"nodemailer": "^6.9.12",
|
|
29
|
+
"twilio": "^3.18.0",
|
|
30
|
+
"uuid": "^2.0.3"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"eslint-config-rallycoding": "^3.2.0",
|
|
34
|
+
"serverless-domain-manager": "^3.3.1",
|
|
35
|
+
"serverless-prune-plugin": "^1.4.1"
|
|
36
|
+
}
|
|
42
37
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
module.exports.newTicketEmailTemplate = {
|
|
2
|
-
|
|
3
|
-
|
|
2
|
+
subject: `New Support Ticket: ___TITLE___`,
|
|
3
|
+
content: `<div style='margin-bottom: 24px; font-size: 14px; line-height: 23px; color: #3e4245;'>
|
|
4
4
|
___NAME___ from ___SITE___ (___CLIENT___) has submitted a new support ticket.
|
|
5
5
|
</div>
|
|
6
6
|
<div style='margin-bottom: 16px; font-size: 22px; line-height: 30px; color: #0a2246; font-weight: bold;'>
|
|
@@ -12,8 +12,8 @@ module.exports.newTicketEmailTemplate = {
|
|
|
12
12
|
};
|
|
13
13
|
|
|
14
14
|
module.exports.statusChangeEmailTemplate = {
|
|
15
|
-
|
|
16
|
-
|
|
15
|
+
subject: `Status Changed on Your Ticket: ___TITLE___`,
|
|
16
|
+
content: `<div style='margin-bottom: 24px; font-size: 14px; line-height: 23px; color: #3e4245;'>
|
|
17
17
|
Your ticket has been updated.
|
|
18
18
|
</div>
|
|
19
19
|
<div style='margin-bottom: 16px; font-size: 13px; line-height: 28px; height: 28px; width: 300px; border-radius: 4px; background-color: ___LABELCOLOR___; color: #fff; text-align: center;'>
|
|
@@ -28,8 +28,8 @@ module.exports.statusChangeEmailTemplate = {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
module.exports.ticketCommentEmailTemplate = {
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
subject: `New Comment on Ticket: ___TITLE___`,
|
|
32
|
+
content: `<div style='margin-bottom: 24px; font-size: 14px; line-height: 23px; color: #3e4245;'>
|
|
33
33
|
There is a new comment on an update:
|
|
34
34
|
</div>
|
|
35
35
|
<div style='margin-bottom: 24px; font-size: 15px; line-height: 22px; border-radius: 4px; border: solid 1px #ccc; padding: 8px; color: #536280; max-width: 500px; display: inline-block;'>
|
|
@@ -44,6 +44,6 @@ module.exports.ticketCommentEmailTemplate = {
|
|
|
44
44
|
};
|
|
45
45
|
|
|
46
46
|
module.exports.plussTeamEmails = [
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
"marty@plusscommunities.com",
|
|
48
|
+
"thor@plusscommunities.com",
|
|
49
49
|
];
|
|
@@ -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,102 +0,0 @@
|
|
|
1
|
-
const jwt = require("jsonwebtoken");
|
|
2
|
-
const jwksClient = require("jwks-rsa");
|
|
3
|
-
|
|
4
|
-
const { getConfig } = require("../../../../../config");
|
|
5
|
-
const { log } = require("../../../../");
|
|
6
|
-
|
|
7
|
-
// Function to retrieve the signing key from Auth0 JWKS
|
|
8
|
-
const getKey = (header, callback) => {
|
|
9
|
-
// Initialize JWKS client
|
|
10
|
-
const client = jwksClient({
|
|
11
|
-
jwksUri: `https://${getConfig().auth0Config.domain}/.well-known/jwks.json`,
|
|
12
|
-
});
|
|
13
|
-
const logId = log("getKey", "header", header);
|
|
14
|
-
client.getSigningKey(header.kid, (err, key) => {
|
|
15
|
-
if (err) {
|
|
16
|
-
log("getKey", "Error:client.getSigningKey", err, logId);
|
|
17
|
-
callback(err, null);
|
|
18
|
-
} else {
|
|
19
|
-
const signingKey = key.publicKey || key.rsaPublicKey;
|
|
20
|
-
log("getKey", "signingKey", signingKey, logId);
|
|
21
|
-
callback(null, signingKey);
|
|
22
|
-
}
|
|
23
|
-
});
|
|
24
|
-
};
|
|
25
|
-
|
|
26
|
-
// Function to retrieve the signing key from the secondary Auth0 JWKS
|
|
27
|
-
const getSecondaryKey = (header, callback) => {
|
|
28
|
-
const logId = log("getSecondaryKey", "header", header);
|
|
29
|
-
|
|
30
|
-
// Initialize secondary JWKS client for the secondary domain
|
|
31
|
-
const secondaryClient = jwksClient({
|
|
32
|
-
jwksUri: `https://${
|
|
33
|
-
getConfig().auth0Config.secondaryDomain
|
|
34
|
-
}/.well-known/jwks.json`,
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
secondaryClient.getSigningKey(header.kid, (err, key) => {
|
|
38
|
-
if (err) {
|
|
39
|
-
log("getSecondaryKey", "Error:secondaryClient.getSigningKey", err, logId);
|
|
40
|
-
callback(err, null);
|
|
41
|
-
} else {
|
|
42
|
-
const signingKey = key.publicKey || key.rsaPublicKey;
|
|
43
|
-
log("getSecondaryKey", "signingKey", signingKey, logId);
|
|
44
|
-
callback(null, signingKey);
|
|
45
|
-
}
|
|
46
|
-
});
|
|
47
|
-
};
|
|
48
|
-
|
|
49
|
-
// Secondary function to verify the access token
|
|
50
|
-
const verifySecondaryAccessToken = (token) => {
|
|
51
|
-
return new Promise((resolve, reject) => {
|
|
52
|
-
jwt.verify(
|
|
53
|
-
token,
|
|
54
|
-
getSecondaryKey,
|
|
55
|
-
{
|
|
56
|
-
audience: getConfig().auth0Config.audience,
|
|
57
|
-
issuer: `https://${getConfig().auth0Config.secondaryDomain}/`,
|
|
58
|
-
algorithms: ["RS256"],
|
|
59
|
-
},
|
|
60
|
-
(err, decoded) => {
|
|
61
|
-
if (err) {
|
|
62
|
-
log("verifySecondaryAccessToken", "Error:jwt.verify", err);
|
|
63
|
-
reject(err);
|
|
64
|
-
} else {
|
|
65
|
-
resolve(decoded); // 'sub' contains the user ID
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
);
|
|
69
|
-
});
|
|
70
|
-
};
|
|
71
|
-
|
|
72
|
-
// Function to validate the token and extract user information
|
|
73
|
-
const decodeAccessToken = async (token) => {
|
|
74
|
-
return new Promise((resolve, reject) => {
|
|
75
|
-
jwt.verify(
|
|
76
|
-
token,
|
|
77
|
-
getKey,
|
|
78
|
-
{
|
|
79
|
-
audience: getConfig().auth0Config.audience,
|
|
80
|
-
issuer: `https://${getConfig().auth0Config.domain}/`,
|
|
81
|
-
algorithms: ["RS256"],
|
|
82
|
-
},
|
|
83
|
-
(err, decoded) => {
|
|
84
|
-
if (err) {
|
|
85
|
-
log("decodeAccessToken", "Error:jwt.verify", err);
|
|
86
|
-
// Attempt to verify with the secondary domain
|
|
87
|
-
verifySecondaryAccessToken(token)
|
|
88
|
-
.then((decodedSecondary) => {
|
|
89
|
-
resolve(decodedSecondary);
|
|
90
|
-
})
|
|
91
|
-
.catch((secondaryErr) => {
|
|
92
|
-
reject(secondaryErr);
|
|
93
|
-
});
|
|
94
|
-
} else {
|
|
95
|
-
resolve(decoded); // 'sub' contains the user ID
|
|
96
|
-
}
|
|
97
|
-
}
|
|
98
|
-
);
|
|
99
|
-
});
|
|
100
|
-
};
|
|
101
|
-
|
|
102
|
-
module.exports = decodeAccessToken;
|