mirror of
https://github.com/mauriceboe/TREK.git
synced 2026-06-30 18:46:00 +00:00
fix: replace JWT tokens in URL query params with short-lived ephemeral tokens
Addresses CWE-598: long-lived JWTs were exposed in WebSocket URLs, file download links, and Immich asset proxy URLs, leaking into server logs, browser history, and Referer headers. - Add ephemeralTokens service: in-memory single-use tokens with per-purpose TTLs (ws=30s, download/immich=60s), max 10k entries, periodic cleanup - Add POST /api/auth/ws-token and POST /api/auth/resource-token endpoints - WebSocket auth now consumes an ephemeral token instead of verifying the JWT directly from the URL; client fetches a fresh token before each connect - File download ?token= query param now accepts ephemeral tokens; Bearer header path continues to accept JWTs for programmatic access - Immich asset proxy replaces authFromQuery JWT injection with ephemeral token consumption - Client: new getAuthUrl() utility, AuthedImg/ImmichImg components, and async onClick handlers replace the synchronous authUrl() pattern throughout FileManager, PlaceInspector, and MemoriesPanel - Add OIDC_DISCOVERY_URL env var and oidc_discovery_url DB setting to allow overriding the auto-constructed discovery endpoint (required for Authentik and similar providers); exposed in the admin UI and .env.example
This commit is contained in:
@@ -1,8 +1,9 @@
|
||||
import express, { Request, Response } from 'express';
|
||||
import express, { Request, Response, NextFunction } from 'express';
|
||||
import { db } from '../db/database';
|
||||
import { authenticate } from '../middleware/auth';
|
||||
import { broadcast } from '../websocket';
|
||||
import { AuthRequest } from '../types';
|
||||
import { consumeEphemeralToken } from '../services/ephemeralTokens';
|
||||
|
||||
const router = express.Router();
|
||||
|
||||
@@ -254,11 +255,16 @@ router.get('/assets/:assetId/info', authenticate, async (req: Request, res: Resp
|
||||
|
||||
// ── Proxy Immich Assets ─────────────────────────────────────────────────────
|
||||
|
||||
// Asset proxy routes accept token via query param (for <img> src usage)
|
||||
function authFromQuery(req: Request, res: Response, next: Function) {
|
||||
const token = req.query.token as string;
|
||||
if (token && !req.headers.authorization) {
|
||||
req.headers.authorization = `Bearer ${token}`;
|
||||
// Asset proxy routes accept ephemeral token via query param (for <img> src usage)
|
||||
function authFromQuery(req: Request, res: Response, next: NextFunction) {
|
||||
const queryToken = req.query.token as string | undefined;
|
||||
if (queryToken) {
|
||||
const userId = consumeEphemeralToken(queryToken, 'immich');
|
||||
if (!userId) return res.status(401).send('Invalid or expired token');
|
||||
const user = db.prepare('SELECT id, username, email, role, mfa_enabled FROM users WHERE id = ?').get(userId) as any;
|
||||
if (!user) return res.status(401).send('User not found');
|
||||
(req as AuthRequest).user = user;
|
||||
return next();
|
||||
}
|
||||
return (authenticate as any)(req, res, next);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user