feat: Allow OAuth users to add passwords

This commit is contained in:
gpt-engineer-app[bot]
2025-10-14 14:45:51 +00:00
parent 6430777dc0
commit 8594291ad2
2 changed files with 69 additions and 21 deletions

View File

@@ -26,6 +26,8 @@ export function SecurityTab() {
const [loadingIdentities, setLoadingIdentities] = useState(true);
const [disconnectingProvider, setDisconnectingProvider] = useState<OAuthProvider | null>(null);
const [passwordSetupProvider, setPasswordSetupProvider] = useState<OAuthProvider | null>(null);
const [hasPassword, setHasPassword] = useState(false);
const [addPasswordMode, setAddPasswordMode] = useState<'standalone' | 'disconnect'>('standalone');
// Load user identities on mount
useEffect(() => {
@@ -33,10 +35,24 @@ export function SecurityTab() {
}, []);
const loadIdentities = async () => {
setLoadingIdentities(true);
const fetchedIdentities = await getUserIdentities();
setIdentities(fetchedIdentities);
setLoadingIdentities(false);
try {
setLoadingIdentities(true);
const fetchedIdentities = await getUserIdentities();
setIdentities(fetchedIdentities);
// Check if user has email/password auth
const hasEmailProvider = fetchedIdentities.some(i => i.provider === 'email');
setHasPassword(hasEmailProvider);
} catch (error) {
console.error('Failed to load identities:', error);
toast({
title: 'Error',
description: 'Failed to load connected accounts',
variant: 'destructive',
});
} finally {
setLoadingIdentities(false);
}
};
const handleSocialLogin = async (provider: OAuthProvider) => {
@@ -63,6 +79,7 @@ export function SecurityTab() {
if (!safetyCheck.canDisconnect) {
if (safetyCheck.reason === 'no_password_backup') {
// Show password setup dialog
setAddPasswordMode('disconnect');
setPasswordSetupProvider(provider);
toast({
title: "Password Required",
@@ -103,21 +120,30 @@ export function SecurityTab() {
};
const handlePasswordSetupSuccess = async () => {
toast({
title: "Password Set",
description: "You can now disconnect your social login."
});
// Refresh identities to show email provider
await loadIdentities();
// Proceed with disconnect
if (passwordSetupProvider) {
if (addPasswordMode === 'disconnect' && passwordSetupProvider) {
toast({
title: "Password Set",
description: "You can now disconnect your social login."
});
await handleUnlinkSocial(passwordSetupProvider);
setPasswordSetupProvider(null);
} else {
toast({
title: 'Password Added',
description: 'You can now sign in using your email and password.',
});
setPasswordSetupProvider(null);
}
};
const handleAddPassword = () => {
setAddPasswordMode('standalone');
setPasswordSetupProvider('google' as OAuthProvider);
};
// Get connected accounts with identity data
const connectedAccounts = [
{
@@ -150,27 +176,38 @@ export function SecurityTab() {
onOpenChange={(open) => !open && setPasswordSetupProvider(null)}
onSuccess={handlePasswordSetupSuccess}
provider={passwordSetupProvider}
mode={addPasswordMode}
/>
)}
<div className="space-y-8">
{/* Change Password */}
{/* Password Section - Conditional based on auth method */}
<div className="space-y-4">
<div className="flex items-center gap-2">
<Key className="w-5 h-5" />
<h3 className="text-lg font-medium">Change Password</h3>
<h3 className="text-lg font-medium">{hasPassword ? 'Change Password' : 'Add Password'}</h3>
</div>
<Card>
<CardHeader>
<CardDescription>
Update your password to keep your account secure. {user?.identities?.some(i => i.provider === 'totp') && 'Two-factor authentication will be required.'}
{hasPassword ? (
<>Update your password to keep your account secure. {user?.identities?.some(i => i.provider === 'totp') && 'Two-factor authentication will be required.'}</>
) : (
'Add password authentication to your account for increased security and backup access.'
)}
</CardDescription>
</CardHeader>
<CardContent>
<Button onClick={() => setPasswordDialogOpen(true)}>
Change Password
</Button>
{hasPassword ? (
<Button onClick={() => setPasswordDialogOpen(true)}>
Change Password
</Button>
) : (
<Button onClick={handleAddPassword}>
Add Password
</Button>
)}
</CardContent>
</Card>
</div>