@openinc/parse-server-opendash 3.10.3 → 3.11.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.
|
@@ -3,7 +3,6 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
3
3
|
exports.init = init;
|
|
4
4
|
const initKanbanStates_1 = require("./kanbanState/initKanbanStates");
|
|
5
5
|
const initMessages_1 = require("./messages/initMessages");
|
|
6
|
-
const initScheduling_1 = require("./schedules/initScheduling");
|
|
7
6
|
const initTicket_1 = require("./ticket/initTicket");
|
|
8
7
|
async function init() {
|
|
9
8
|
console.log("Initializing open.SERVICE feature...");
|
|
@@ -11,7 +10,7 @@ async function init() {
|
|
|
11
10
|
await (0, initKanbanStates_1.initCurrentTicketStates)();
|
|
12
11
|
await (0, initTicket_1.updateTicketDescription)();
|
|
13
12
|
await (0, initKanbanStates_1.initEnabledFlag)();
|
|
14
|
-
await
|
|
13
|
+
// await initScheduling();
|
|
15
14
|
await (0, initTicket_1.initTicketClass)();
|
|
16
15
|
console.log("open.SERVICE feature initialized.");
|
|
17
16
|
}
|
|
@@ -7,7 +7,6 @@ exports.init = init;
|
|
|
7
7
|
const crypto_1 = require("crypto");
|
|
8
8
|
const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
|
|
9
9
|
const jwks_rsa_1 = __importDefault(require("jwks-rsa"));
|
|
10
|
-
const https_1 = __importDefault(require("https"));
|
|
11
10
|
const config_1 = require("../features/config");
|
|
12
11
|
const types_1 = require("../types");
|
|
13
12
|
const tenantId = config_1.ConfigInstance.getInstance().get("MICROSOFT_TENANT_ID") ||
|
|
@@ -16,222 +15,67 @@ const tenantId = config_1.ConfigInstance.getInstance().get("MICROSOFT_TENANT_ID"
|
|
|
16
15
|
const appId = config_1.ConfigInstance.getInstance().get("MICROSOFT_APP_ID") ||
|
|
17
16
|
process.env.MICROSOFT_APP_ID ||
|
|
18
17
|
process.env.OI_MICROSOFT_APP_ID;
|
|
19
|
-
//
|
|
20
|
-
const getTenantJwksUri = () => `https://login.microsoftonline.com/${tenantId}/discovery/v2.0/keys`;
|
|
21
|
-
const getCommonJwksUri = () => `https://login.microsoftonline.com/common/discovery/v2.0/keys`;
|
|
18
|
+
// Simple, standard Microsoft v2.0 endpoint with app-specific parameter
|
|
22
19
|
const client = (0, jwks_rsa_1.default)({
|
|
23
|
-
jwksUri:
|
|
24
|
-
cache:
|
|
25
|
-
|
|
20
|
+
jwksUri: `https://login.microsoftonline.com/${tenantId}/discovery/keys?appid=${appId}`,
|
|
21
|
+
cache: true,
|
|
22
|
+
cacheMaxAge: 12 * 60 * 60 * 1000, // 12 hours
|
|
23
|
+
timeout: 30000,
|
|
26
24
|
});
|
|
27
|
-
// Fallback client for common endpoint if tenant-specific fails
|
|
28
|
-
const clientCommon = (0, jwks_rsa_1.default)({
|
|
29
|
-
jwksUri: getCommonJwksUri(),
|
|
30
|
-
cache: false, // Disable cache temporarily for debugging
|
|
31
|
-
timeout: 30000, // 30 seconds
|
|
32
|
-
});
|
|
33
|
-
// Manual JWKS fetch function to bypass jwks-rsa library issues
|
|
34
|
-
function fetchJWKSManually(uri) {
|
|
35
|
-
return new Promise((resolve, reject) => {
|
|
36
|
-
console.log("Making manual HTTPS request to:", uri);
|
|
37
|
-
https_1.default
|
|
38
|
-
.get(uri, (res) => {
|
|
39
|
-
let data = "";
|
|
40
|
-
console.log("Response status:", res.statusCode);
|
|
41
|
-
console.log("Response headers:", res.headers);
|
|
42
|
-
res.on("data", (chunk) => {
|
|
43
|
-
data += chunk;
|
|
44
|
-
});
|
|
45
|
-
res.on("end", () => {
|
|
46
|
-
try {
|
|
47
|
-
const jwks = JSON.parse(data);
|
|
48
|
-
console.log("Successfully fetched JWKS manually, found", jwks.keys?.length, "keys");
|
|
49
|
-
resolve(jwks);
|
|
50
|
-
}
|
|
51
|
-
catch (error) {
|
|
52
|
-
console.error("Failed to parse JWKS JSON:", error);
|
|
53
|
-
reject(error);
|
|
54
|
-
}
|
|
55
|
-
});
|
|
56
|
-
})
|
|
57
|
-
.on("error", (error) => {
|
|
58
|
-
console.error("HTTPS request failed:", error);
|
|
59
|
-
reject(error);
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
}
|
|
63
|
-
// Convert JWK to PEM format
|
|
64
|
-
function jwkToPem(jwk) {
|
|
65
|
-
// For Microsoft tokens, we can use the x5c (certificate chain) directly
|
|
66
|
-
if (jwk.x5c && jwk.x5c[0]) {
|
|
67
|
-
return `-----BEGIN CERTIFICATE-----\n${jwk.x5c[0]}\n-----END CERTIFICATE-----`;
|
|
68
|
-
}
|
|
69
|
-
// If no x5c, we'd need to construct from n and e, but Microsoft usually provides x5c
|
|
70
|
-
throw new Error("No x5c certificate chain found in JWK - cannot convert to PEM");
|
|
71
|
-
}
|
|
72
25
|
function getKey(header, callback) {
|
|
73
|
-
console.log("JWT Header:", header);
|
|
74
|
-
console.log("Tenant ID:", tenantId);
|
|
75
|
-
console.log("App ID:", appId);
|
|
76
26
|
if (!header.kid) {
|
|
77
|
-
console.error("No kid found in token header");
|
|
78
27
|
return callback(new Error("No kid found in token header"));
|
|
79
28
|
}
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
if (!key) {
|
|
87
|
-
console.log("Key not found in tenant-specific JWKS, trying common endpoint...");
|
|
88
|
-
return fetchJWKSManually(getCommonJwksUri()).then((commonJwks) => {
|
|
89
|
-
const commonKey = commonJwks.keys.find((k) => k.kid === header.kid);
|
|
90
|
-
if (!commonKey) {
|
|
91
|
-
throw new Error(`Key with kid ${header.kid} not found in either endpoint`);
|
|
92
|
-
}
|
|
93
|
-
return commonKey;
|
|
94
|
-
});
|
|
95
|
-
}
|
|
96
|
-
return key; // Return the key directly, not wrapped
|
|
97
|
-
})
|
|
98
|
-
.then((key) => {
|
|
99
|
-
if (!key) {
|
|
100
|
-
throw new Error("No key found");
|
|
101
|
-
}
|
|
102
|
-
// Convert to PEM format using x5c if available
|
|
103
|
-
if (key.x5c && key.x5c[0]) {
|
|
104
|
-
const cert = jwkToPem(key);
|
|
105
|
-
console.log("Successfully retrieved signing key using manual fetch");
|
|
106
|
-
callback(null, cert);
|
|
107
|
-
}
|
|
108
|
-
else {
|
|
109
|
-
throw new Error("No x5c certificate chain found in key");
|
|
110
|
-
}
|
|
111
|
-
})
|
|
112
|
-
.catch((manualError) => {
|
|
113
|
-
console.error("Manual JWKS fetch failed, falling back to jwks-rsa library:", manualError);
|
|
114
|
-
// Fallback to original jwks-rsa approach
|
|
115
|
-
console.log("Trying tenant-specific JWKS endpoint with jwks-rsa...");
|
|
116
|
-
console.log("Tenant JWKS URI:", getTenantJwksUri());
|
|
117
|
-
client.getSigningKey(header.kid, (err, key) => {
|
|
118
|
-
if (err) {
|
|
119
|
-
console.error("Error with tenant-specific endpoint, trying common endpoint:", err);
|
|
120
|
-
console.log("Trying common JWKS endpoint as fallback...");
|
|
121
|
-
console.log("Common JWKS URI:", getCommonJwksUri());
|
|
122
|
-
// Try common endpoint as fallback
|
|
123
|
-
clientCommon.getSigningKey(header.kid, (errCommon, keyCommon) => {
|
|
124
|
-
if (errCommon) {
|
|
125
|
-
console.error("Error with common endpoint:", errCommon);
|
|
126
|
-
return callback(errCommon);
|
|
127
|
-
}
|
|
128
|
-
if (!keyCommon) {
|
|
129
|
-
console.error("No key returned from common JWKS client");
|
|
130
|
-
return callback(new Error("Key not found in common endpoint"));
|
|
131
|
-
}
|
|
132
|
-
try {
|
|
133
|
-
const signingKey = keyCommon.getPublicKey();
|
|
134
|
-
console.log("Successfully retrieved signing key from common endpoint");
|
|
135
|
-
callback(null, signingKey);
|
|
136
|
-
}
|
|
137
|
-
catch (keyError) {
|
|
138
|
-
console.error("Error extracting public key from common endpoint:", keyError);
|
|
139
|
-
callback(keyError instanceof Error
|
|
140
|
-
? keyError
|
|
141
|
-
: new Error(String(keyError)));
|
|
142
|
-
}
|
|
143
|
-
});
|
|
144
|
-
return;
|
|
145
|
-
}
|
|
146
|
-
if (!key) {
|
|
147
|
-
console.error("No key returned from tenant-specific JWKS client");
|
|
148
|
-
return callback(new Error("Key not found"));
|
|
149
|
-
}
|
|
150
|
-
try {
|
|
151
|
-
const signingKey = key.getPublicKey();
|
|
152
|
-
console.log("Successfully retrieved signing key from tenant-specific endpoint");
|
|
153
|
-
callback(null, signingKey);
|
|
154
|
-
}
|
|
155
|
-
catch (keyError) {
|
|
156
|
-
console.error("Error extracting public key:", keyError);
|
|
157
|
-
callback(keyError instanceof Error ? keyError : new Error(String(keyError)));
|
|
158
|
-
}
|
|
159
|
-
});
|
|
29
|
+
client.getSigningKey(header.kid, (err, key) => {
|
|
30
|
+
if (err)
|
|
31
|
+
return callback(err);
|
|
32
|
+
if (!key)
|
|
33
|
+
return callback(new Error("Key not found"));
|
|
34
|
+
callback(null, key.getPublicKey());
|
|
160
35
|
});
|
|
161
36
|
}
|
|
162
37
|
async function init(name) {
|
|
163
38
|
Parse.Cloud.define(name, async (request) => {
|
|
164
|
-
console.log("Logging in with microsoft...");
|
|
165
39
|
const token = request.params.token;
|
|
166
40
|
const account = request.params.account;
|
|
167
|
-
const defaultTenant = await new Parse.Query(types_1.Tenant)
|
|
168
|
-
.ascending("createdAt")
|
|
169
|
-
.first({ useMasterKey: true });
|
|
170
41
|
if (!token) {
|
|
171
42
|
throw new Parse.Error(Parse.Error.INVALID_JSON, "Token missing");
|
|
172
43
|
}
|
|
173
|
-
// Validate Microsoft configuration
|
|
174
44
|
if (!tenantId || !appId) {
|
|
175
|
-
console.error("Missing Microsoft configuration:", {
|
|
176
|
-
tenantId: !!tenantId,
|
|
177
|
-
appId: !!appId,
|
|
178
|
-
});
|
|
179
45
|
throw new Parse.Error(Parse.Error.INVALID_JSON, "Microsoft authentication not properly configured");
|
|
180
46
|
}
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
47
|
+
// DEBUGGING: Decode token without verification to see what we're dealing with
|
|
48
|
+
console.log("=== DEBUGGING TOKEN ===");
|
|
49
|
+
try {
|
|
50
|
+
const tokenParts = token.split(".");
|
|
51
|
+
const payload = JSON.parse(Buffer.from(tokenParts[1], "base64").toString());
|
|
52
|
+
console.log("Token audience (aud):", payload.aud);
|
|
53
|
+
console.log("Token issuer (iss):", payload.iss);
|
|
184
54
|
console.log("Expected audience:", appId);
|
|
185
55
|
console.log("Expected issuer:", `https://login.microsoftonline.com/${tenantId}/v2.0`);
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
aud: payload.aud,
|
|
195
|
-
iss: payload.iss,
|
|
196
|
-
tid: payload.tid,
|
|
197
|
-
ver: payload.ver,
|
|
198
|
-
oid: payload.oid,
|
|
199
|
-
preferred_username: payload.preferred_username,
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
catch (decodeError) {
|
|
204
|
-
console.error("Failed to decode token for debugging:", decodeError);
|
|
205
|
-
}
|
|
206
|
-
console.log("Verifying JWT with the following settings:");
|
|
207
|
-
console.log("- Audience:", appId);
|
|
208
|
-
console.log("- Issuer:", `https://login.microsoftonline.com/${tenantId}/v2.0`);
|
|
209
|
-
console.log("- Algorithm: RS256");
|
|
56
|
+
console.log("Audience match:", payload.aud === appId);
|
|
57
|
+
console.log("Issuer match:", payload.iss === `https://login.microsoftonline.com/${tenantId}/v2.0`);
|
|
58
|
+
}
|
|
59
|
+
catch (e) {
|
|
60
|
+
console.error("Failed to decode token:", e);
|
|
61
|
+
}
|
|
62
|
+
console.log("=== END DEBUGGING ===");
|
|
63
|
+
const verifiedPayload = await new Promise((resolve, reject) => {
|
|
210
64
|
jsonwebtoken_1.default.verify(token, getKey, {
|
|
211
65
|
audience: appId,
|
|
212
66
|
issuer: `https://login.microsoftonline.com/${tenantId}/v2.0`,
|
|
213
|
-
algorithms: ["RS256"],
|
|
67
|
+
algorithms: ["RS256"],
|
|
214
68
|
}, (err, decoded) => {
|
|
215
69
|
if (err) {
|
|
216
|
-
console.error("JWT verification failed:", err);
|
|
217
|
-
console.error("Error name:", err.name);
|
|
218
|
-
console.error("Error message:", err.message);
|
|
219
|
-
if (err.name === "TokenExpiredError") {
|
|
220
|
-
console.error("Token has expired");
|
|
221
|
-
}
|
|
222
|
-
else if (err.name === "JsonWebTokenError") {
|
|
223
|
-
console.error("Invalid token");
|
|
224
|
-
}
|
|
225
|
-
else if (err.name === "NotBeforeError") {
|
|
226
|
-
console.error("Token not active yet");
|
|
227
|
-
}
|
|
70
|
+
console.error("JWT verification failed:", err.message);
|
|
228
71
|
return reject(err);
|
|
229
72
|
}
|
|
230
|
-
console.log("JWT verification successful");
|
|
231
|
-
console.log("Verified payload contains oid:", decoded?.oid);
|
|
232
73
|
resolve(decoded);
|
|
233
74
|
});
|
|
234
75
|
});
|
|
76
|
+
const defaultTenant = await new Parse.Query(types_1.Tenant)
|
|
77
|
+
.ascending("createdAt")
|
|
78
|
+
.first({ useMasterKey: true });
|
|
235
79
|
let user = await new Parse.Query(Parse.User)
|
|
236
80
|
.equalTo("username", verifiedPayload.oid)
|
|
237
81
|
.first({ useMasterKey: true });
|
|
@@ -239,7 +83,7 @@ async function init(name) {
|
|
|
239
83
|
user = new Parse.User();
|
|
240
84
|
user.set("username", verifiedPayload.oid);
|
|
241
85
|
user.set("email", account.username);
|
|
242
|
-
user.set("password", (0, crypto_1.randomBytes)(16).toString("hex"));
|
|
86
|
+
user.set("password", (0, crypto_1.randomBytes)(16).toString("hex"));
|
|
243
87
|
user.set("name", verifiedPayload.name || verifiedPayload.preferred_username);
|
|
244
88
|
user.set("tenant", defaultTenant);
|
|
245
89
|
user = await user.signUp(null, { useMasterKey: true });
|
|
@@ -249,7 +93,7 @@ async function init(name) {
|
|
|
249
93
|
const session = new Parse.Object("_Session");
|
|
250
94
|
session.set("user", user);
|
|
251
95
|
session.set("sessionToken", sessionToken);
|
|
252
|
-
session.set("expiresAt", new Date(Date.now() + 24 * 60 * 60 * 1000 * 7));
|
|
96
|
+
session.set("expiresAt", new Date(Date.now() + 24 * 60 * 60 * 1000 * 7));
|
|
253
97
|
const savedSession = await session.save(null, { useMasterKey: true });
|
|
254
98
|
return savedSession.get("sessionToken");
|
|
255
99
|
});
|