mirror of
https://github.com/bulwarkmail/webmail.git
synced 2026-11-04 08:15:41 +00:00
An impersonation handoff sealed `<target>%<master>` and the master password into the session cookie, and `PUT /api/auth/session` hands that cookie's contents to the browser on every restore. Anyone who received a handoff could read a credential that signs in as every mailbox and into Stalwart administration, with no expiry and no audit trail. The impersonate route now uses the master credential server-side, once, to create an app password on the target mailbox that expires after 8 hours. Only that app password reaches the cookies. Signing out revokes it through a sealed grant cookie, and PUT refuses any session that still holds the configured master password, so cookies minted before this change are dropped instead of returned.
30 lines
1.0 KiB
TypeScript
30 lines
1.0 KiB
TypeScript
import { decryptPayload, encryptPayload } from '@/lib/auth/crypto';
|
|
|
|
/**
|
|
* Remembers which app password an impersonation handoff minted, so signing
|
|
* out can revoke it instead of leaving it valid until it expires.
|
|
* Impersonation always lands in slot 0, so one cookie is enough.
|
|
*/
|
|
export const IMPERSONATION_GRANT_COOKIE = 'jmap_impersonation';
|
|
|
|
export interface ImpersonationGrant {
|
|
serverUrl: string;
|
|
mailbox: string;
|
|
credentialId: string;
|
|
}
|
|
|
|
export function sealImpersonationGrant(grant: ImpersonationGrant): string {
|
|
return encryptPayload({ ...grant }, 'impersonation-grant');
|
|
}
|
|
|
|
export function openImpersonationGrant(token: string | undefined): ImpersonationGrant | null {
|
|
if (!token) return null;
|
|
const payload = decryptPayload(token, 'impersonation-grant');
|
|
if (!payload) return null;
|
|
const { serverUrl, mailbox, credentialId } = payload;
|
|
if (typeof serverUrl !== 'string' || typeof mailbox !== 'string' || typeof credentialId !== 'string') {
|
|
return null;
|
|
}
|
|
return { serverUrl, mailbox, credentialId };
|
|
}
|