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) => ({