everydaycats commited on
Commit
f9c93a3
·
verified ·
1 Parent(s): 91ec2e3

Update app.js

Browse files
Files changed (1) hide show
  1. app.js +34 -107
app.js CHANGED
@@ -121,7 +121,7 @@ app.post('/redeem', async (req, res) => {
121
  const data = tempKeys.get(key);
122
  const sessionSecret = uuidv4();
123
 
124
- // UPDATED: Set expiresIn to '3d' (3 days)
125
  const token = jwt.sign(
126
  { uid: data.uid, projectId: data.projectId },
127
  sessionSecret,
@@ -141,12 +141,42 @@ app.post('/redeem', async (req, res) => {
141
  res.json({ token });
142
  });
143
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  app.post('/poll', async (req, res) => {
145
  const { token, payload: clientPayload } = req.body;
146
 
147
  if (!token) return res.status(400).json({ error: 'Token required' });
148
 
149
- // Decode without verify first to find user/project ID
150
  const decoded = jwt.decode(token);
151
  if (!decoded || !decoded.uid || !decoded.projectId) {
152
  return res.status(401).json({ error: 'Malformed token' });
@@ -159,11 +189,8 @@ app.post('/poll', async (req, res) => {
159
  }
160
 
161
  try {
162
- // 1. Verify Signature and Expiration (checks exp claim)
163
  const verifiedData = jwt.verify(token, secret);
164
 
165
- // 2. EXPLICIT CHECK: Ensure token is not older than 3 days
166
- // Even if expiresIn was 7d, this blocks it after 3d.
167
  const threeDaysInSeconds = 3 * 24 * 60 * 60;
168
  const nowInSeconds = Math.floor(Date.now() / 1000);
169
 
@@ -177,7 +204,6 @@ app.post('/poll', async (req, res) => {
177
  try {
178
  const response = await axios.post(externalUrl, {
179
  projectId: verifiedData.projectId,
180
- // Add client data if needed
181
  // data: clientPayload
182
  });
183
 
@@ -194,9 +220,8 @@ app.post('/poll', async (req, res) => {
194
  }
195
  });
196
 
197
- // Changed to .all or .post because the HTML script sends a POST request
198
  app.get('/cleanup', (req, res) => {
199
- const THRESHOLD = 4 * 1000 * 60 * 60; // 4 Hours
200
  const now = Date.now();
201
  let cleanedCount = 0;
202
 
@@ -212,8 +237,6 @@ app.get('/cleanup', (req, res) => {
212
  tempKeys.delete(key);
213
  }
214
  }
215
-
216
- // console.log(`🧹 Garbage Collection: Removed ${cleanedCount} cached sessions.`);
217
  res.json({ message: `Cleaned ${cleanedCount} cached sessions from memory.` });
218
  });
219
 
@@ -223,10 +246,8 @@ app.post('/nullify', verifyFirebaseUser, async (req, res) => {
223
 
224
  const cacheKey = `${req.user.uid}:${projectId}`;
225
 
226
- // 1. Remove Active Session (JWT secret)
227
  const existedInMemory = activeSessions.delete(cacheKey);
228
 
229
- // 2. Remove Temporal Tokens (Pending Keys)
230
  let deletedTempKeys = 0;
231
  for (const [tKey, tData] of tempKeys.entries()) {
232
  if (tData.uid === req.user.uid && tData.projectId === projectId) {
@@ -235,7 +256,6 @@ app.post('/nullify', verifyFirebaseUser, async (req, res) => {
235
  }
236
  }
237
 
238
- // 3. Remove from DB
239
  if (db) {
240
  try {
241
  await db.ref(`plugin_oauth/${req.user.uid}/${projectId}`).remove();
@@ -253,101 +273,8 @@ app.post('/nullify', verifyFirebaseUser, async (req, res) => {
253
  });
254
  });
255
 
256
- // ---------------------------------------------------------
257
- // 5. EMBEDDED HTML DEBUGGER
258
- // ---------------------------------------------------------
259
  app.get('/', (req, res) => {
260
- res.send(`
261
- <!DOCTYPE html>
262
- <html lang="en">
263
- <head>
264
- <meta charset="UTF-8">
265
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
266
- <title>Roblox Plugin Auth Server</title>
267
- <style>
268
- body { font-family: monospace; background: #1e1e1e; color: #d4d4d4; padding: 20px; max-width: 800px; margin: 0 auto; }
269
- .container { background: #252526; padding: 20px; border-radius: 8px; border: 1px solid #333; }
270
- input, button { padding: 10px; margin: 5px 0; width: 100%; box-sizing: border-box; background: #3c3c3c; border: 1px solid #555; color: white; }
271
- button { background: #0e639c; cursor: pointer; font-weight: bold; }
272
- button:hover { background: #1177bb; }
273
- button.danger { background: #a51d2d; }
274
- label { display: block; margin-top: 10px; color: #9cdcfe; }
275
- pre { background: #000; padding: 10px; border: 1px solid #444; overflow-x: auto; white-space: pre-wrap; }
276
- </style>
277
- </head>
278
- <body>
279
- <h1>🔌 Plugin Auth Debugger</h1>
280
- <div class="container">
281
- <h3>1. Configuration</h3>
282
- <label>Firebase ID Token</label>
283
- <input type="text" id="fbToken" placeholder="eyJhbG...">
284
- <label>Project ID</label>
285
- <input type="text" id="projId" value="roblox_world_1">
286
- <hr style="border-color:#444">
287
-
288
- <h3>2. Actions</h3>
289
- <button onclick="generateKey()">Generate PROJ_ Key</button>
290
- <pre id="keyResult">...</pre>
291
-
292
- <label>Redeem Key</label>
293
- <input type="text" id="redeemKeyInput">
294
- <button onclick="redeemKey()">Redeem for JWT</button>
295
- <pre id="jwtResult">...</pre>
296
-
297
- <label>Poll (JWT)</label>
298
- <input type="text" id="jwtInput">
299
- <button onclick="poll()">Poll External Server</button>
300
- <pre id="pollResult">...</pre>
301
-
302
- <div style="display:flex; gap:10px; margin-top:10px;">
303
- <button onclick="cleanup()">Memory Cleanup</button>
304
- <button class="danger" onclick="nullify()">Nullify (Nuke)</button>
305
- </div>
306
- <pre id="mgmtResult">...</pre>
307
- </div>
308
- <script>
309
- const baseUrl = '';
310
- async function generateKey() {
311
- const token = document.getElementById('fbToken').value;
312
- const projectId = document.getElementById('projId').value;
313
- const headers = {};
314
- if(token) headers['Authorization'] = 'Bearer ' + token;
315
- const res = await fetch(baseUrl + '/key', { method: 'POST', headers: { 'Content-Type': 'application/json', ...headers }, body: JSON.stringify({ projectId }) });
316
- const data = await res.json();
317
- document.getElementById('keyResult').innerText = JSON.stringify(data, null, 2);
318
- if(data.key) document.getElementById('redeemKeyInput').value = data.key;
319
- }
320
- async function redeemKey() {
321
- const key = document.getElementById('redeemKeyInput').value;
322
- const res = await fetch(baseUrl + '/redeem', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ key }) });
323
- const data = await res.json();
324
- document.getElementById('jwtResult').innerText = JSON.stringify(data, null, 2);
325
- if(data.token) document.getElementById('jwtInput').value = data.token;
326
- }
327
- async function poll() {
328
- const token = document.getElementById('jwtInput').value;
329
- const res = await fetch(baseUrl + '/poll', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ token, payload: { msg: "Hello" } }) });
330
- const data = await res.json();
331
- document.getElementById('pollResult').innerText = JSON.stringify(data, null, 2);
332
- }
333
- async function cleanup() {
334
- const res = await fetch(baseUrl + '/cleanup', { method: 'GET' });
335
- const data = await res.json();
336
- document.getElementById('mgmtResult').innerText = JSON.stringify(data, null, 2);
337
- }
338
- async function nullify() {
339
- const token = document.getElementById('fbToken').value;
340
- const projectId = document.getElementById('projId').value;
341
- const headers = {};
342
- if(token) headers['Authorization'] = 'Bearer ' + token;
343
- const res = await fetch(baseUrl + '/nullify', { method: 'POST', headers: { 'Content-Type': 'application/json', ...headers }, body: JSON.stringify({ projectId }) });
344
- const data = await res.json();
345
- document.getElementById('mgmtResult').innerText = JSON.stringify(data, null, 2);
346
- }
347
- </script>
348
- </body>
349
- </html>
350
- `);
351
  });
352
 
353
  const PORT = process.env.PORT || 7860;
 
121
  const data = tempKeys.get(key);
122
  const sessionSecret = uuidv4();
123
 
124
+ // Set expiresIn to '3d' (3 days)
125
  const token = jwt.sign(
126
  { uid: data.uid, projectId: data.projectId },
127
  sessionSecret,
 
141
  res.json({ token });
142
  });
143
 
144
+ // --- NEW ENDPOINT: Simple Verification Check ---
145
+ app.post('/verify', async (req, res) => {
146
+ const { token } = req.body;
147
+ if (!token) return res.status(400).json({ valid: false, error: 'Token required' });
148
+
149
+ const decoded = jwt.decode(token);
150
+ if (!decoded || !decoded.uid || !decoded.projectId) {
151
+ return res.status(401).json({ valid: false, error: 'Malformed token' });
152
+ }
153
+
154
+ const secret = await getSessionSecret(decoded.uid, decoded.projectId);
155
+
156
+ if (!secret) {
157
+ return res.status(401).json({ valid: false, error: 'Session revoked' });
158
+ }
159
+
160
+ try {
161
+ jwt.verify(token, secret);
162
+ // Explicitly check 3 day max age
163
+ const threeDaysInSeconds = 3 * 24 * 60 * 60;
164
+ const nowInSeconds = Math.floor(Date.now() / 1000);
165
+ if (decoded.iat && (nowInSeconds - decoded.iat > threeDaysInSeconds)) {
166
+ return res.status(403).json({ valid: false, error: 'Expired' });
167
+ }
168
+
169
+ return res.json({ valid: true });
170
+ } catch (err) {
171
+ return res.status(403).json({ valid: false, error: 'Invalid signature' });
172
+ }
173
+ });
174
+
175
  app.post('/poll', async (req, res) => {
176
  const { token, payload: clientPayload } = req.body;
177
 
178
  if (!token) return res.status(400).json({ error: 'Token required' });
179
 
 
180
  const decoded = jwt.decode(token);
181
  if (!decoded || !decoded.uid || !decoded.projectId) {
182
  return res.status(401).json({ error: 'Malformed token' });
 
189
  }
190
 
191
  try {
 
192
  const verifiedData = jwt.verify(token, secret);
193
 
 
 
194
  const threeDaysInSeconds = 3 * 24 * 60 * 60;
195
  const nowInSeconds = Math.floor(Date.now() / 1000);
196
 
 
204
  try {
205
  const response = await axios.post(externalUrl, {
206
  projectId: verifiedData.projectId,
 
207
  // data: clientPayload
208
  });
209
 
 
220
  }
221
  });
222
 
 
223
  app.get('/cleanup', (req, res) => {
224
+ const THRESHOLD = 1000 * 60 * 60; // 1 Hour
225
  const now = Date.now();
226
  let cleanedCount = 0;
227
 
 
237
  tempKeys.delete(key);
238
  }
239
  }
 
 
240
  res.json({ message: `Cleaned ${cleanedCount} cached sessions from memory.` });
241
  });
242
 
 
246
 
247
  const cacheKey = `${req.user.uid}:${projectId}`;
248
 
 
249
  const existedInMemory = activeSessions.delete(cacheKey);
250
 
 
251
  let deletedTempKeys = 0;
252
  for (const [tKey, tData] of tempKeys.entries()) {
253
  if (tData.uid === req.user.uid && tData.projectId === projectId) {
 
256
  }
257
  }
258
 
 
259
  if (db) {
260
  try {
261
  await db.ref(`plugin_oauth/${req.user.uid}/${projectId}`).remove();
 
273
  });
274
  });
275
 
 
 
 
276
  app.get('/', (req, res) => {
277
+ res.send('Plugin Auth Server Running');
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
278
  });
279
 
280
  const PORT = process.env.PORT || 7860;