Added recovery keys

This commit is contained in:
Bryan1029384756
2026-02-18 09:24:53 -06:00
parent bebf0bf989
commit ce9902d95d
16 changed files with 642 additions and 44 deletions

View File

@@ -17,6 +17,7 @@ const THEME_PREVIEWS = {
const TABS = [
{ id: 'account', label: 'My Account', section: 'USER SETTINGS' },
{ id: 'security', label: 'Security', section: 'USER SETTINGS' },
{ id: 'appearance', label: 'Appearance', section: 'USER SETTINGS' },
{ id: 'voice', label: 'Voice & Video', section: 'APP SETTINGS' },
{ id: 'keybinds', label: 'Keybinds', section: 'APP SETTINGS' },
@@ -111,6 +112,7 @@ const UserSettings = ({ onClose, userId, username, onLogout }) => {
<div style={{ flex: 1, display: 'flex', justifyContent: 'flex-start', overflowY: 'auto' }}>
<div style={{ flex: 1, maxWidth: '740px', padding: '60px 40px 80px', position: 'relative' }}>
{activeTab === 'account' && <MyAccountTab userId={userId} username={username} />}
{activeTab === 'security' && <SecurityTab />}
{activeTab === 'appearance' && <AppearanceTab />}
{activeTab === 'voice' && <VoiceVideoTab />}
{activeTab === 'keybinds' && <KeybindsTab />}
@@ -550,6 +552,173 @@ const MyAccountTab = ({ userId, username }) => {
);
};
/* =========================================
SECURITY TAB
========================================= */
const SecurityTab = () => {
const [masterKey, setMasterKey] = useState(null);
const [revealed, setRevealed] = useState(false);
const [confirmed, setConfirmed] = useState(false);
const [copied, setCopied] = useState(false);
useEffect(() => {
const mk = sessionStorage.getItem('masterKey');
setMasterKey(mk);
}, []);
const formatKey = (hex) => {
if (!hex) return '';
return hex.match(/.{1,4}/g).join(' ');
};
const handleCopy = () => {
if (!masterKey) return;
navigator.clipboard.writeText(masterKey).then(() => {
setCopied(true);
setTimeout(() => setCopied(false), 2000);
});
};
const handleDownload = () => {
if (!masterKey) return;
const content = [
'=== RECOVERY KEY ===',
'',
'This is your Recovery Key for your encrypted account.',
'Store it in a safe place. If you lose your password, this key is the ONLY way to recover your account.',
'',
'DO NOT share this key with anyone.',
'',
`Recovery Key: ${masterKey}`,
'',
`Exported: ${new Date().toISOString()}`,
].join('\n');
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'recovery-key.txt';
a.click();
URL.revokeObjectURL(url);
};
const labelStyle = {
display: 'block', color: 'var(--header-secondary)', fontSize: '12px',
fontWeight: '700', textTransform: 'uppercase', marginBottom: '8px',
};
return (
<div>
<h2 style={{ color: 'var(--header-primary)', margin: '0 0 20px', fontSize: '20px' }}>Security</h2>
<div style={{ backgroundColor: 'var(--bg-secondary)', borderRadius: '8px', padding: '20px' }}>
<h3 style={{ color: 'var(--header-primary)', margin: '0 0 8px', fontSize: '16px', fontWeight: '600' }}>
Recovery Key
</h3>
<p style={{ color: 'var(--text-muted)', fontSize: '14px', margin: '0 0 16px', lineHeight: '1.4' }}>
Your Recovery Key allows you to reset your password without losing access to your encrypted messages.
Store it somewhere safe if you forget your password, this is the <strong style={{ color: 'var(--text-normal)' }}>only way</strong> to recover your account.
</p>
{!masterKey ? (
<div style={{
backgroundColor: 'var(--bg-tertiary)', borderRadius: '4px', padding: '16px',
color: 'var(--text-muted)', fontSize: '14px',
}}>
Recovery Key is not available in this session. Please log out and log back in to access it.
</div>
) : !revealed ? (
<div>
{/* Warning box */}
<div style={{
backgroundColor: 'rgba(250, 166, 26, 0.1)', border: '1px solid rgba(250, 166, 26, 0.4)',
borderRadius: '4px', padding: '12px', marginBottom: '16px',
}}>
<div style={{ color: '#faa61a', fontSize: '14px', fontWeight: '600', marginBottom: '4px' }}>
Warning
</div>
<div style={{ color: 'var(--text-normal)', fontSize: '13px', lineHeight: '1.4' }}>
Anyone with your Recovery Key can reset your password and take control of your account.
Only reveal it in a private, secure environment.
</div>
</div>
<label style={{
display: 'flex', alignItems: 'center', gap: '8px', cursor: 'pointer',
color: 'var(--text-normal)', fontSize: '14px', marginBottom: '16px',
}}>
<input
type="checkbox"
checked={confirmed}
onChange={(e) => setConfirmed(e.target.checked)}
style={{ width: '16px', height: '16px', accentColor: 'var(--brand-experiment)' }}
/>
I understand and want to reveal my Recovery Key
</label>
<button
onClick={() => setRevealed(true)}
disabled={!confirmed}
style={{
backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none',
borderRadius: '4px', padding: '10px 20px', cursor: confirmed ? 'pointer' : 'not-allowed',
fontSize: '14px', fontWeight: '500', opacity: confirmed ? 1 : 0.5,
}}
>
Reveal Recovery Key
</button>
</div>
) : (
<div>
<label style={labelStyle}>Your Recovery Key</label>
<div style={{
backgroundColor: 'var(--bg-tertiary)', borderRadius: '4px', padding: '16px',
fontFamily: 'Consolas, "Courier New", monospace', fontSize: '15px',
color: 'var(--text-normal)', wordBreak: 'break-all', lineHeight: '1.6',
letterSpacing: '1px', marginBottom: '12px', userSelect: 'all',
}}>
{formatKey(masterKey)}
</div>
<div style={{ display: 'flex', gap: '8px', marginBottom: '16px' }}>
<button
onClick={handleCopy}
style={{
backgroundColor: 'var(--brand-experiment)', color: 'white', border: 'none',
borderRadius: '4px', padding: '8px 16px', cursor: 'pointer',
fontSize: '14px', fontWeight: '500',
}}
>
{copied ? 'Copied!' : 'Copy'}
</button>
<button
onClick={handleDownload}
style={{
backgroundColor: 'var(--bg-tertiary)', color: 'var(--text-normal)', border: 'none',
borderRadius: '4px', padding: '8px 16px', cursor: 'pointer',
fontSize: '14px', fontWeight: '500',
}}
>
Download
</button>
<button
onClick={() => { setRevealed(false); setConfirmed(false); }}
style={{
backgroundColor: 'transparent', color: 'var(--text-muted)', border: '1px solid var(--border-subtle)',
borderRadius: '4px', padding: '8px 16px', cursor: 'pointer',
fontSize: '14px', fontWeight: '500',
}}
>
Hide
</button>
</div>
</div>
)}
</div>
</div>
);
};
/* =========================================
APPEARANCE TAB
========================================= */