tabminal 3.0.27 → 3.0.29
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/AGENTS.md +13 -7
- package/package.json +1 -1
- package/public/app.js +682 -123
- package/public/index.html +28 -0
- package/public/modules/url-auth.js +57 -35
- package/public/styles.css +113 -0
- package/src/auth.mjs +471 -37
- package/src/persistence.mjs +75 -0
- package/src/server.mjs +99 -1
package/src/server.mjs
CHANGED
|
@@ -18,7 +18,17 @@ import { TerminalManager } from './terminal-manager.mjs';
|
|
|
18
18
|
import { AcpManager } from './acp-manager.mjs';
|
|
19
19
|
import { SystemMonitor } from './system-monitor.mjs';
|
|
20
20
|
import { config } from './config.mjs';
|
|
21
|
-
import {
|
|
21
|
+
import {
|
|
22
|
+
authMiddleware,
|
|
23
|
+
initAuthStore,
|
|
24
|
+
issueAuthTokensFromPasswordHash,
|
|
25
|
+
listAuthSessions,
|
|
26
|
+
refreshAuthTokens,
|
|
27
|
+
revokeAuthSessionById,
|
|
28
|
+
revokeOtherAuthSessions,
|
|
29
|
+
revokeAuthTokens,
|
|
30
|
+
verifyClient
|
|
31
|
+
} from './auth.mjs';
|
|
22
32
|
import {
|
|
23
33
|
setupFsRoutes,
|
|
24
34
|
writeTextFileSnapshot
|
|
@@ -174,6 +184,61 @@ router.get('/healthz', (ctx) => {
|
|
|
174
184
|
ctx.body = { status: 'ok' };
|
|
175
185
|
});
|
|
176
186
|
|
|
187
|
+
router.post('/api/auth/login', async (ctx) => {
|
|
188
|
+
const body = ctx.request.body || {};
|
|
189
|
+
const passwordHash = typeof body.passwordHash === 'string'
|
|
190
|
+
? body.passwordHash
|
|
191
|
+
: '';
|
|
192
|
+
const result = await issueAuthTokensFromPasswordHash(passwordHash, {
|
|
193
|
+
userAgent: ctx.get('user-agent')
|
|
194
|
+
});
|
|
195
|
+
ctx.status = result.status;
|
|
196
|
+
if (result.ok) {
|
|
197
|
+
ctx.body = {
|
|
198
|
+
accessToken: result.accessToken,
|
|
199
|
+
accessTokenExpiresAt: result.accessTokenExpiresAt,
|
|
200
|
+
refreshToken: result.refreshToken,
|
|
201
|
+
refreshTokenExpiresAt: result.refreshTokenExpiresAt
|
|
202
|
+
};
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
ctx.body = { error: result.error };
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
router.post('/api/auth/refresh', async (ctx) => {
|
|
209
|
+
const body = ctx.request.body || {};
|
|
210
|
+
const refreshToken = typeof body.refreshToken === 'string'
|
|
211
|
+
? body.refreshToken
|
|
212
|
+
: '';
|
|
213
|
+
const result = await refreshAuthTokens(refreshToken, {
|
|
214
|
+
userAgent: ctx.get('user-agent')
|
|
215
|
+
});
|
|
216
|
+
ctx.status = result.status;
|
|
217
|
+
if (result.ok) {
|
|
218
|
+
ctx.body = {
|
|
219
|
+
accessToken: result.accessToken,
|
|
220
|
+
accessTokenExpiresAt: result.accessTokenExpiresAt,
|
|
221
|
+
refreshToken: result.refreshToken,
|
|
222
|
+
refreshTokenExpiresAt: result.refreshTokenExpiresAt
|
|
223
|
+
};
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
ctx.body = { error: result.error };
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
router.post('/api/auth/logout', async (ctx) => {
|
|
230
|
+
const body = ctx.request.body || {};
|
|
231
|
+
const refreshToken = typeof body.refreshToken === 'string'
|
|
232
|
+
? body.refreshToken
|
|
233
|
+
: '';
|
|
234
|
+
const accessToken = ctx.get('Authorization') || ctx.query.token || '';
|
|
235
|
+
const result = await revokeAuthTokens({
|
|
236
|
+
refreshToken,
|
|
237
|
+
accessToken
|
|
238
|
+
});
|
|
239
|
+
ctx.status = result.status;
|
|
240
|
+
});
|
|
241
|
+
|
|
177
242
|
app.use(async (ctx, next) => {
|
|
178
243
|
if (ctx.method === 'GET' && ctx.path === '/api/version') {
|
|
179
244
|
ctx.set(
|
|
@@ -199,6 +264,39 @@ app.use(bodyParser());
|
|
|
199
264
|
// Auth Middleware for API routes
|
|
200
265
|
app.use(authMiddleware);
|
|
201
266
|
|
|
267
|
+
await initAuthStore();
|
|
268
|
+
|
|
269
|
+
router.get('/api/auth/session', async (ctx) => {
|
|
270
|
+
const auth = ctx.state.auth || {};
|
|
271
|
+
ctx.body = {
|
|
272
|
+
authenticated: true,
|
|
273
|
+
sessionId: auth.sessionId || '',
|
|
274
|
+
accessTokenExpiresAt: auth.accessTokenExpiresAt || '',
|
|
275
|
+
refreshTokenExpiresAt: auth.refreshTokenExpiresAt || ''
|
|
276
|
+
};
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
router.get('/api/auth/sessions', async (ctx) => {
|
|
280
|
+
const auth = ctx.state.auth || {};
|
|
281
|
+
ctx.body = {
|
|
282
|
+
sessions: await listAuthSessions(auth.sessionId || '')
|
|
283
|
+
};
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
router.delete('/api/auth/sessions/:id', async (ctx) => {
|
|
287
|
+
const result = await revokeAuthSessionById(ctx.params.id);
|
|
288
|
+
ctx.status = result.status;
|
|
289
|
+
if (!result.ok) {
|
|
290
|
+
ctx.body = { error: result.error };
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
router.post('/api/auth/logout-others', async (ctx) => {
|
|
295
|
+
const auth = ctx.state.auth || {};
|
|
296
|
+
const result = await revokeOtherAuthSessions(auth.sessionId || '');
|
|
297
|
+
ctx.status = result.status;
|
|
298
|
+
});
|
|
299
|
+
|
|
202
300
|
const systemMonitor = new SystemMonitor();
|
|
203
301
|
const terminalManager = new TerminalManager();
|
|
204
302
|
const acpManager = new AcpManager({ terminalManager });
|