@openinc/parse-server-opendash 3.10.2 → 3.10.3

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,6 +7,7 @@ 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"));
10
11
  const config_1 = require("../features/config");
11
12
  const types_1 = require("../types");
12
13
  const tenantId = config_1.ConfigInstance.getInstance().get("MICROSOFT_TENANT_ID") ||
@@ -15,23 +16,59 @@ const tenantId = config_1.ConfigInstance.getInstance().get("MICROSOFT_TENANT_ID"
15
16
  const appId = config_1.ConfigInstance.getInstance().get("MICROSOFT_APP_ID") ||
16
17
  process.env.MICROSOFT_APP_ID ||
17
18
  process.env.OI_MICROSOFT_APP_ID;
18
- // Setup JWKS client for Microsoft - try v2.0 first, fallback to v1.0
19
- const getJwksUri = (version = "v2.0") => `https://login.microsoftonline.com/${tenantId}/discovery/${version}/keys`;
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`;
20
22
  const client = (0, jwks_rsa_1.default)({
21
- jwksUri: getJwksUri(),
22
- cache: true,
23
- cacheMaxEntries: 5,
24
- cacheMaxAge: 1000 * 60 * 60 * 24, // 24 hours
23
+ jwksUri: getTenantJwksUri(),
24
+ cache: false, // Disable cache temporarily for debugging
25
25
  timeout: 30000, // 30 seconds
26
26
  });
27
- // Fallback client for v1.0 if v2.0 fails
28
- const clientV1 = (0, jwks_rsa_1.default)({
29
- jwksUri: getJwksUri("v1.0"),
30
- cache: true,
31
- cacheMaxEntries: 5,
32
- cacheMaxAge: 1000 * 60 * 60 * 24, // 24 hours
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
33
31
  timeout: 30000, // 30 seconds
34
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
+ }
35
72
  function getKey(header, callback) {
36
73
  console.log("JWT Header:", header);
37
74
  console.log("Tenant ID:", tenantId);
@@ -41,44 +78,85 @@ function getKey(header, callback) {
41
78
  return callback(new Error("No kid found in token header"));
42
79
  }
43
80
  console.log("Attempting to get signing key for kid:", header.kid);
44
- client.getSigningKey(header.kid, (err, key) => {
45
- if (err) {
46
- console.error("Error with v2.0 endpoint, trying v1.0:", err);
47
- // Try v1.0 endpoint as fallback
48
- clientV1.getSigningKey(header.kid, (errV1, keyV1) => {
49
- if (errV1) {
50
- console.error("Error with v1.0 endpoint:", errV1);
51
- return callback(errV1);
52
- }
53
- if (!keyV1) {
54
- console.error("No key returned from v1.0 JWKS client");
55
- return callback(new Error("Key not found in v1.0 endpoint"));
56
- }
57
- try {
58
- const signingKey = keyV1.getPublicKey();
59
- console.log("Successfully retrieved signing key from v1.0 endpoint");
60
- callback(null, signingKey);
61
- }
62
- catch (keyError) {
63
- console.error("Error extracting public key from v1.0:", keyError);
64
- callback(keyError instanceof Error ? keyError : new Error(String(keyError)));
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`);
65
92
  }
93
+ return commonKey;
66
94
  });
67
- return;
68
95
  }
96
+ return key; // Return the key directly, not wrapped
97
+ })
98
+ .then((key) => {
69
99
  if (!key) {
70
- console.error("No key returned from JWKS client");
71
- return callback(new Error("Key not found"));
100
+ throw new Error("No key found");
72
101
  }
73
- try {
74
- const signingKey = key.getPublicKey();
75
- console.log("Successfully retrieved signing key");
76
- callback(null, signingKey);
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);
77
107
  }
78
- catch (keyError) {
79
- console.error("Error extracting public key:", keyError);
80
- callback(keyError instanceof Error ? keyError : new Error(String(keyError)));
108
+ else {
109
+ throw new Error("No x5c certificate chain found in key");
81
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
+ });
82
160
  });
83
161
  }
84
162
  async function init(name) {
@@ -117,12 +195,18 @@ async function init(name) {
117
195
  iss: payload.iss,
118
196
  tid: payload.tid,
119
197
  ver: payload.ver,
198
+ oid: payload.oid,
199
+ preferred_username: payload.preferred_username,
120
200
  });
121
201
  }
122
202
  }
123
203
  catch (decodeError) {
124
204
  console.error("Failed to decode token for debugging:", decodeError);
125
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");
126
210
  jsonwebtoken_1.default.verify(token, getKey, {
127
211
  audience: appId,
128
212
  issuer: `https://login.microsoftonline.com/${tenantId}/v2.0`,
@@ -132,9 +216,19 @@ async function init(name) {
132
216
  console.error("JWT verification failed:", err);
133
217
  console.error("Error name:", err.name);
134
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
+ }
135
228
  return reject(err);
136
229
  }
137
230
  console.log("JWT verification successful");
231
+ console.log("Verified payload contains oid:", decoded?.oid);
138
232
  resolve(decoded);
139
233
  });
140
234
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openinc/parse-server-opendash",
3
- "version": "3.10.2",
3
+ "version": "3.10.3",
4
4
  "description": "Parse Server Cloud Code for open.INC Stack.",
5
5
  "packageManager": "pnpm@10.13.1",
6
6
  "keywords": [