fix(web): address auth review findings (open redirect, dead code, HMAC per-req)
Pre-merge review of the P0 branch: - Open redirect: login redirected to an unvalidated ?next param, so a crafted /login?next=https://evil (or //evil) bounced off-site after login. Now only same-origin relative paths are honored (else fall back to "/"). - Remove the unused useRouter import/binding in the login form. - Memoize expectedToken() keyed on (password, secret) so middleware stops recomputing an HMAC on every gated request. Deferred (low-value/latent, noted): Soulseek cover.jpg isn't retrieved into staging (UI still shows Cover Art Archive art); _retrieve's basename-keyed map could undercount same-named files but source_refs come from a single slskd dir; SignOut visibility rides a server-env read (cosmetic). web 138 tests green, tsc + build clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
+8
-1
@@ -13,11 +13,16 @@ function toB64(bytes: Uint8Array): string {
|
||||
return btoa(s);
|
||||
}
|
||||
|
||||
// Memoize the derived token so the middleware doesn't recompute an HMAC on every request
|
||||
// (password/secret don't change at runtime; keyed on both so a change still recomputes).
|
||||
let _cache: { password: string; secret: string; token: string } | null = null;
|
||||
|
||||
/** The token a valid session cookie must carry, or "" when auth is disabled (no password). */
|
||||
export async function expectedToken(): Promise<string> {
|
||||
const password = process.env.LYRA_PASSWORD;
|
||||
if (!password) return "";
|
||||
const secret = process.env.LYRA_SECRET_KEY ?? "lyra";
|
||||
if (_cache && _cache.password === password && _cache.secret === secret) return _cache.token;
|
||||
const enc = new TextEncoder();
|
||||
const key = await crypto.subtle.importKey(
|
||||
"raw",
|
||||
@@ -27,7 +32,9 @@ export async function expectedToken(): Promise<string> {
|
||||
["sign"],
|
||||
);
|
||||
const sig = await crypto.subtle.sign("HMAC", key, enc.encode(password));
|
||||
return toB64(new Uint8Array(sig));
|
||||
const token = toB64(new Uint8Array(sig));
|
||||
_cache = { password, secret, token };
|
||||
return token;
|
||||
}
|
||||
|
||||
/** Constant-time string compare (both operands are our own derived tokens). */
|
||||
|
||||
Reference in New Issue
Block a user