@openinc/parse-server-opendash 3.10.3 → 3.10.4

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.
@@ -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
- // Setup JWKS client for Microsoft - use tenant-specific endpoint with common as fallback
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: getTenantJwksUri(),
24
- cache: false, // Disable cache temporarily for debugging
25
- timeout: 30000, // 30 seconds
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
- console.log("Attempting to get signing key for kid:", header.kid);
81
- // Try manual fetch first to bypass jwks-rsa issues
82
- console.log("Trying manual JWKS fetch...");
83
- fetchJWKSManually(getTenantJwksUri())
84
- .then((jwks) => {
85
- const key = jwks.keys.find((k) => k.kid === header.kid);
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
- const verifiedPayload = await new Promise((resolve, reject) => {
182
- console.log("Starting JWT verification...");
183
- console.log("Token length:", token.length);
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
- // Decode token payload for debugging (without verification)
187
- try {
188
- const tokenParts = token.split(".");
189
- if (tokenParts.length === 3) {
190
- const header = JSON.parse(Buffer.from(tokenParts[0], "base64").toString());
191
- const payload = JSON.parse(Buffer.from(tokenParts[1], "base64").toString());
192
- console.log("Token header (unverified):", header);
193
- console.log("Token payload (unverified):", {
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"], // Microsoft uses 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")); // Generate a random password
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)); // 7 day expiration
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
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openinc/parse-server-opendash",
3
- "version": "3.10.3",
3
+ "version": "3.10.4",
4
4
  "description": "Parse Server Cloud Code for open.INC Stack.",
5
5
  "packageManager": "pnpm@10.13.1",
6
6
  "keywords": [