mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-04 16:14:07 +00:00
A wipe by name took any username and server with no session behind it. It now needs a sealed cookie the account got while it was signed in, since a dropped account has no session cookie left. The old wipe test asserted that a name alone was enough, which is the behaviour being removed.
43 lines
1.6 KiB
TypeScript
43 lines
1.6 KiB
TypeScript
/**
|
|
* Proof that this browser had a session for an account, for the desktop app.
|
|
*
|
|
* An account the app dropped has no session cookie left, and its plugin secrets
|
|
* still sit on this device until a wipe. So when a signed-in account stores or lists
|
|
* them, it also gets this sealed cookie, and a wipe by name needs the one for that
|
|
* name. Nobody can then clear an account's secrets by knowing its address.
|
|
*
|
|
* It opens nothing else, and only the desktop build writes or reads it.
|
|
*/
|
|
|
|
import type { NextRequest, NextResponse } from 'next/server';
|
|
import { decryptPayload, encryptPayload } from '@/lib/auth/crypto';
|
|
import { getCookieOptions } from '@/lib/oauth/cookie-config';
|
|
import { userKey, type VaultUser } from './vault';
|
|
|
|
/** Chromium's upper limit. A dropped account can stay dropped for a long time. */
|
|
const MAX_AGE_SECONDS = 400 * 24 * 60 * 60;
|
|
|
|
function cookieName(key: string): string {
|
|
return `jmap_psp_${key.slice(0, 16)}`;
|
|
}
|
|
|
|
export function setWipeProof(response: NextResponse, user: VaultUser): void {
|
|
const key = userKey(user);
|
|
response.cookies.set(cookieName(key), encryptPayload({ u: key }, 'plugin-wipe-proof'), { ...getCookieOptions(), maxAge: MAX_AGE_SECONDS });
|
|
}
|
|
|
|
export function clearWipeProof(response: NextResponse, user: VaultUser): void {
|
|
response.cookies.set(cookieName(userKey(user)), '', { ...getCookieOptions(), maxAge: 0 });
|
|
}
|
|
|
|
export function hasWipeProof(request: NextRequest, user: VaultUser): boolean {
|
|
const key = userKey(user);
|
|
const value = request.cookies.get(cookieName(key))?.value;
|
|
if (!value) return false;
|
|
try {
|
|
return decryptPayload(value, 'plugin-wipe-proof')?.u === key;
|
|
} catch {
|
|
return false;
|
|
}
|
|
}
|