diff --git a/components/settings/account-security-settings.tsx b/components/settings/account-security-settings.tsx index 27578bf2..51b3a972 100644 --- a/components/settings/account-security-settings.tsx +++ b/components/settings/account-security-settings.tsx @@ -2,7 +2,7 @@ import { useState, useEffect, useCallback } from 'react'; import { useTranslations } from 'next-intl'; -import { Shield, Key, Smartphone, Lock, Trash2, Plus, Eye, EyeOff, Copy, Check, Loader2 } from 'lucide-react'; +import { Shield, Key, Smartphone, Lock, Trash2, Plus, Eye, EyeOff, Copy, Check, Loader2, Monitor } from 'lucide-react'; import { Button } from '@/components/ui/button'; import { Input } from '@/components/ui/input'; import { SettingsSection, SettingItem, ToggleSwitch } from './settings-section'; @@ -440,20 +440,74 @@ function EncryptionSection() { ); } +function EmailClientSection() { + const t = useTranslations('settings.security'); + const { client } = useAuthStore(); + const [copied, setCopied] = useState(false); + + const jmapUsername = client?.getUsername() || ''; + + const handleCopy = () => { + navigator.clipboard.writeText(jmapUsername).then(() => { + setCopied(true); + setTimeout(() => setCopied(false), 2000); + }); + }; + + return ( +
+
+ +

{t('email_client.title')}

+
+

{t('email_client.description')}

+
+
+ +
+ + +
+
+

{t('email_client.password_instructions')}

+
+
+ ); +} + export function AccountSecuritySettings() { const t = useTranslations('settings.security'); - const { isStalwart, isProbing, probe, fetchAll } = useAccountSecurityStore(); - const { isAuthenticated } = useAuthStore(); + const { isStalwart, isProbing, probe, fetchAll, fetchAuthInfo } = useAccountSecurityStore(); + const { isAuthenticated, authMode } = useAuthStore(); + const isOAuth = authMode === 'oauth'; useEffect(() => { if (isAuthenticated && isStalwart === null) { probe().then((detected) => { if (detected) { - fetchAll(); + if (isOAuth) { + fetchAuthInfo(); + } else { + fetchAll(); + } } }); } - }, [isAuthenticated, isStalwart, probe, fetchAll]); + }, [isAuthenticated, isStalwart, probe, fetchAll, fetchAuthInfo, isOAuth]); if (isProbing) { return ( @@ -477,40 +531,44 @@ export function AccountSecuritySettings() { return (
- {/* Password Change */} - + {!isOAuth && ( + <> + +
+ +
+
+
+ +

{t('totp.section_title')}

+
+ +
+
+ + )} -
- - {/* Display Name */} - - -
- - {/* Two-Factor Authentication */} -
-
- -

{t('totp.section_title')}

-
- -
- -
- - {/* App Passwords */} -
+ {isOAuth && ( + <> +
+ + + )} - {/* Encryption at Rest */} -
-
- -

{t('encryption.section_title')}

-
- -
+ {!isOAuth && ( + <> +
+
+
+ +

{t('encryption.section_title')}

+
+ +
+ + )}
); diff --git a/locales/en/common.json b/locales/en/common.json index 64bafe70..8a17b927 100644 --- a/locales/en/common.json +++ b/locales/en/common.json @@ -1002,6 +1002,14 @@ "enabled": "Encryption at rest enabled", "disabled_success": "Encryption at rest disabled", "error": "Failed to update encryption settings" + }, + "email_client": { + "title": "Email Client Setup", + "description": "Use these credentials to configure your desktop or mobile email client (Thunderbird, Apple Mail, Outlook, etc.)", + "jmap_username_label": "JMAP Username", + "copy": "Copy", + "copied": "Copied", + "password_instructions": "Use your JMAP username above along with an app password to sign in to your email client. Create an app password in the section above if you haven't already." } }, "identities": { diff --git a/stores/account-security-store.ts b/stores/account-security-store.ts index d293a8ed..409cda41 100644 --- a/stores/account-security-store.ts +++ b/stores/account-security-store.ts @@ -1,5 +1,6 @@ import { create } from 'zustand'; import { debug } from '@/lib/debug'; +import { useAuthStore } from './auth-store'; interface AccountSecurityState { // Detection @@ -42,14 +43,14 @@ interface AccountSecurityState { clearState: () => void; } -/** - * Get authorization headers for API requests. - * Returns headers object with auth credentials. - */ function getApiHeaders(): Record { - // We rely on the proxy routes which read from session cookie - // No additional headers needed for cookie-based auth - return {}; + const { client } = useAuthStore.getState(); + if (!client) return {}; + return { + 'Authorization': client.getAuthHeader(), + 'X-JMAP-Server-URL': client.getServerUrl(), + 'X-JMAP-Username': client.getUsername(), + }; } export const useAccountSecurityStore = create()((set, get) => ({