feat: implement "Remember Me" functionality with AES-256 encryption for secure credential storage

This commit is contained in:
2026-02-14 18:31:14 +07:00
parent ea5fc0a617
commit 057c7885cf
7 changed files with 859 additions and 8 deletions

111
src/utils/rememberMe.ts Normal file
View File

@@ -0,0 +1,111 @@
/**
* Utility functions for managing "Remember me" password saving functionality
* Uses AES-256 encryption for secure credential storage
*/
import CryptoJS from 'crypto-js';
import {
getRememberMeData,
removeRememberMeData,
setRememberMeData,
} from './storage';
const SECRET_KEY = 'smatec_secret_key_2024_secure_encryption';
export interface RememberedCredentials {
email: string;
password: string;
}
/**
* AES-256 encryption using CryptoJS
* Automatically generates random IV and encodes to Base64
* @param data Data to encrypt
* @returns Encrypted string (base64 encoded with IV)
*/
function encrypt(data: string): string {
try {
const encrypted = CryptoJS.AES.encrypt(data, SECRET_KEY).toString();
return encrypted;
} catch (error) {
console.error('Encryption error:', error);
return '';
}
}
/**
* AES-256 decryption using CryptoJS
* Automatically extracts IV and decrypts
* @param encrypted Encrypted data (base64 encoded with IV)
* @returns Decrypted string
*/
function decrypt(encrypted: string): string {
try {
const decrypted = CryptoJS.AES.decrypt(encrypted, SECRET_KEY);
const result = decrypted.toString(CryptoJS.enc.Utf8);
return result;
} catch (error) {
console.error('Decryption error:', error);
return '';
}
}
/**
* Clear saved credentials from localStorage
*/
export function clearCredentials(): void {
removeRememberMeData();
}
/**
* Save credentials to localStorage (encrypted with secret key)
* @param email User's email
* @param password User's password
*/
export function saveCredentials(email: string, password: string): void {
try {
const credentials: RememberedCredentials = { email, password };
const json = JSON.stringify(credentials);
const encrypted = encrypt(json);
setRememberMeData(encrypted);
} catch (error) {
console.error('Error saving credentials:', error);
}
}
/**
* Load credentials from localStorage
* @returns RememberedCredentials or null if not found or error occurs
*/
export function loadCredentials(): RememberedCredentials | null {
try {
const encrypted = getRememberMeData();
if (!encrypted) {
return null;
}
const decrypted = decrypt(encrypted);
if (!decrypted) {
clearCredentials();
return null;
}
const credentials: RememberedCredentials = JSON.parse(decrypted);
return credentials;
} catch (error) {
console.error('Error loading credentials:', error);
clearCredentials();
return null;
}
}
/**
* Check if there are saved credentials
* @returns true if credentials exist, false otherwise
*/
export function hasSavedCredentials(): boolean {
try {
const encrypted = getRememberMeData();
return encrypted !== null;
} catch (error) {
return false;
}
}

View File

@@ -1,6 +1,7 @@
import {
ACCESS_TOKEN,
REFRESH_TOKEN,
REMEMBER_ME_KEY,
TERMINAL_THEME_KEY,
THEME_KEY,
} from '@/constants';
@@ -70,19 +71,62 @@ export function getBrowserId() {
return id;
}
// ============================================
// Remember Me Storage Operations
// ============================================
/**
* Get encrypted credentials from localStorage
*/
export function getRememberMeData(): string | null {
try {
return localStorage.getItem(REMEMBER_ME_KEY);
} catch (error) {
console.error('Error reading remember me data:', error);
return null;
}
}
/**
* Save encrypted credentials to localStorage
*/
export function setRememberMeData(encryptedData: string): void {
try {
localStorage.setItem(REMEMBER_ME_KEY, encryptedData);
} catch (error) {
console.error('Error saving remember me data:', error);
}
}
/**
* Remove encrypted credentials from localStorage
*/
export function removeRememberMeData(): void {
try {
localStorage.removeItem(REMEMBER_ME_KEY);
} catch (error) {
console.error('Error removing remember me data:', error);
}
}
/**
* Clear all localStorage data except browserId, theme and terminal theme
*/
export function clearAllData() {
const browserId = localStorage.getItem('sip-browserid');
const rememberMe = getRememberMeData();
const theme = getTheme();
const terminalTheme = getTerminalTheme();
localStorage.clear();
// Khôi phục các giá trị cần thiết
if (browserId) {
localStorage.setItem('sip-browserid', browserId);
}
localStorage.setItem(THEME_KEY, theme);
localStorage.setItem(TERMINAL_THEME_KEY, terminalTheme);
if (rememberMe) {
setRememberMeData(rememberMe);
}
setTheme(theme);
setTerminalTheme(terminalTheme);
}
/**