Persist auth session token in secure cookie storage
This commit is contained in:
@@ -1,5 +1,5 @@
|
|||||||
// Maintainer-controlled web client version.
|
// Maintainer-controlled web client version.
|
||||||
// Format: YYYY.MM.DD Rn (example: 2026.02.20 R2)
|
// Format: YYYY.MM.DD Rn (example: 2026.02.20 R2)
|
||||||
window.CHGRID_WEB_VERSION = "2026.02.28 R314";
|
window.CHGRID_WEB_VERSION = "2026.02.28 R315";
|
||||||
// Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid.
|
// Optional display timezone for timestamps. Falls back to America/Detroit if unset/invalid.
|
||||||
window.CHGRID_TIME_ZONE = "America/Detroit";
|
window.CHGRID_TIME_ZONE = "America/Detroit";
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ const MASTER_VOLUME_STORAGE_KEY = 'chatGridMasterVolume';
|
|||||||
const PEER_LISTEN_GAINS_STORAGE_KEY = 'chatGridPeerListenGains';
|
const PEER_LISTEN_GAINS_STORAGE_KEY = 'chatGridPeerListenGains';
|
||||||
const NICKNAME_STORAGE_KEY = 'spatialChatNickname';
|
const NICKNAME_STORAGE_KEY = 'spatialChatNickname';
|
||||||
const AUTH_USERNAME_STORAGE_KEY = 'chatGridAuthUsername';
|
const AUTH_USERNAME_STORAGE_KEY = 'chatGridAuthUsername';
|
||||||
|
const LEGACY_AUTH_SESSION_TOKEN_STORAGE_KEY = 'chatGridAuthSessionToken';
|
||||||
|
const AUTH_SESSION_COOKIE_NAME = 'chgrid_session_token';
|
||||||
|
const AUTH_SESSION_MAX_AGE_SECONDS = 14 * 24 * 60 * 60;
|
||||||
|
|
||||||
type DevicePreference = {
|
type DevicePreference = {
|
||||||
id: string;
|
id: string;
|
||||||
@@ -27,6 +30,33 @@ type AudioDevicePreferences = {
|
|||||||
* Wraps localStorage reads/writes for client user settings.
|
* Wraps localStorage reads/writes for client user settings.
|
||||||
*/
|
*/
|
||||||
export class SettingsStore {
|
export class SettingsStore {
|
||||||
|
private readCookie(name: string): string {
|
||||||
|
const cookie = document.cookie || '';
|
||||||
|
const parts = cookie.split(';');
|
||||||
|
for (const part of parts) {
|
||||||
|
const [rawKey, ...rest] = part.trim().split('=');
|
||||||
|
if (rawKey !== name) continue;
|
||||||
|
const rawValue = rest.join('=');
|
||||||
|
try {
|
||||||
|
return decodeURIComponent(rawValue);
|
||||||
|
} catch {
|
||||||
|
return rawValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
private writeCookie(name: string, value: string, maxAgeSeconds: number): void {
|
||||||
|
const encoded = encodeURIComponent(value);
|
||||||
|
const secure = window.location.protocol === 'https:' ? '; Secure' : '';
|
||||||
|
document.cookie = `${name}=${encoded}; Path=/; Max-Age=${Math.max(0, Math.floor(maxAgeSeconds))}; SameSite=Lax${secure}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
private clearCookie(name: string): void {
|
||||||
|
const secure = window.location.protocol === 'https:' ? '; Secure' : '';
|
||||||
|
document.cookie = `${name}=; Path=/; Max-Age=0; SameSite=Lax${secure}`;
|
||||||
|
}
|
||||||
|
|
||||||
loadEffectLevels(): Partial<Record<'reverb' | 'echo' | 'flanger' | 'high_pass' | 'low_pass' | 'off', number>> | null {
|
loadEffectLevels(): Partial<Record<'reverb' | 'echo' | 'flanger' | 'high_pass' | 'low_pass' | 'off', number>> | null {
|
||||||
const raw = localStorage.getItem(EFFECT_LEVELS_STORAGE_KEY);
|
const raw = localStorage.getItem(EFFECT_LEVELS_STORAGE_KEY);
|
||||||
if (!raw) return null;
|
if (!raw) return null;
|
||||||
@@ -115,17 +145,18 @@ export class SettingsStore {
|
|||||||
}
|
}
|
||||||
|
|
||||||
loadAuthSessionToken(): string {
|
loadAuthSessionToken(): string {
|
||||||
// Session tokens are intentionally not persisted in browser storage.
|
// Session token is persisted in cookie storage (not localStorage).
|
||||||
// Remove any legacy stored token and force fresh auth on reload.
|
localStorage.removeItem(LEGACY_AUTH_SESSION_TOKEN_STORAGE_KEY);
|
||||||
localStorage.removeItem('chatGridAuthSessionToken');
|
return this.readCookie(AUTH_SESSION_COOKIE_NAME);
|
||||||
return '';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
saveAuthSessionToken(token: string): void {
|
saveAuthSessionToken(token: string): void {
|
||||||
// Session tokens are intentionally not persisted in browser storage.
|
localStorage.removeItem(LEGACY_AUTH_SESSION_TOKEN_STORAGE_KEY);
|
||||||
// Keep behavior explicit: always clear any legacy token slot.
|
if (token) {
|
||||||
void token;
|
this.writeCookie(AUTH_SESSION_COOKIE_NAME, token, AUTH_SESSION_MAX_AGE_SECONDS);
|
||||||
localStorage.removeItem('chatGridAuthSessionToken');
|
return;
|
||||||
|
}
|
||||||
|
this.clearCookie(AUTH_SESSION_COOKIE_NAME);
|
||||||
}
|
}
|
||||||
|
|
||||||
loadAuthUsername(): string {
|
loadAuthUsername(): string {
|
||||||
|
|||||||
Reference in New Issue
Block a user