mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-22 11:11:14 -05:00
feat: Allow OAuth users to add passwords
This commit is contained in:
@@ -19,14 +19,16 @@ interface PasswordSetupDialogProps {
|
|||||||
open: boolean;
|
open: boolean;
|
||||||
onOpenChange: (open: boolean) => void;
|
onOpenChange: (open: boolean) => void;
|
||||||
onSuccess: () => void;
|
onSuccess: () => void;
|
||||||
provider: OAuthProvider;
|
provider?: OAuthProvider;
|
||||||
|
mode?: 'standalone' | 'disconnect';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function PasswordSetupDialog({
|
export function PasswordSetupDialog({
|
||||||
open,
|
open,
|
||||||
onOpenChange,
|
onOpenChange,
|
||||||
onSuccess,
|
onSuccess,
|
||||||
provider
|
provider,
|
||||||
|
mode = 'standalone'
|
||||||
}: PasswordSetupDialogProps) {
|
}: PasswordSetupDialogProps) {
|
||||||
const [password, setPassword] = useState('');
|
const [password, setPassword] = useState('');
|
||||||
const [confirmPassword, setConfirmPassword] = useState('');
|
const [confirmPassword, setConfirmPassword] = useState('');
|
||||||
@@ -73,8 +75,17 @@ export function PasswordSetupDialog({
|
|||||||
<DialogTitle>Set Up Password</DialogTitle>
|
<DialogTitle>Set Up Password</DialogTitle>
|
||||||
</div>
|
</div>
|
||||||
<DialogDescription>
|
<DialogDescription>
|
||||||
You need to set a password before disconnecting your {provider} account.
|
{mode === 'disconnect' && provider ? (
|
||||||
This ensures you can still access your account.
|
<>
|
||||||
|
You need to set a password before disconnecting your {provider} account.
|
||||||
|
This ensures you can still access your account.
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
Add a password to your account for increased security. You'll be able to
|
||||||
|
sign in using either your password or your connected social accounts.
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</DialogDescription>
|
</DialogDescription>
|
||||||
</DialogHeader>
|
</DialogHeader>
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,8 @@ export function SecurityTab() {
|
|||||||
const [loadingIdentities, setLoadingIdentities] = useState(true);
|
const [loadingIdentities, setLoadingIdentities] = useState(true);
|
||||||
const [disconnectingProvider, setDisconnectingProvider] = useState<OAuthProvider | null>(null);
|
const [disconnectingProvider, setDisconnectingProvider] = useState<OAuthProvider | null>(null);
|
||||||
const [passwordSetupProvider, setPasswordSetupProvider] = 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
|
// Load user identities on mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -33,10 +35,24 @@ export function SecurityTab() {
|
|||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const loadIdentities = async () => {
|
const loadIdentities = async () => {
|
||||||
setLoadingIdentities(true);
|
try {
|
||||||
const fetchedIdentities = await getUserIdentities();
|
setLoadingIdentities(true);
|
||||||
setIdentities(fetchedIdentities);
|
const fetchedIdentities = await getUserIdentities();
|
||||||
setLoadingIdentities(false);
|
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) => {
|
const handleSocialLogin = async (provider: OAuthProvider) => {
|
||||||
@@ -63,6 +79,7 @@ export function SecurityTab() {
|
|||||||
if (!safetyCheck.canDisconnect) {
|
if (!safetyCheck.canDisconnect) {
|
||||||
if (safetyCheck.reason === 'no_password_backup') {
|
if (safetyCheck.reason === 'no_password_backup') {
|
||||||
// Show password setup dialog
|
// Show password setup dialog
|
||||||
|
setAddPasswordMode('disconnect');
|
||||||
setPasswordSetupProvider(provider);
|
setPasswordSetupProvider(provider);
|
||||||
toast({
|
toast({
|
||||||
title: "Password Required",
|
title: "Password Required",
|
||||||
@@ -103,21 +120,30 @@ export function SecurityTab() {
|
|||||||
};
|
};
|
||||||
|
|
||||||
const handlePasswordSetupSuccess = async () => {
|
const handlePasswordSetupSuccess = async () => {
|
||||||
toast({
|
|
||||||
title: "Password Set",
|
|
||||||
description: "You can now disconnect your social login."
|
|
||||||
});
|
|
||||||
|
|
||||||
// Refresh identities to show email provider
|
// Refresh identities to show email provider
|
||||||
await loadIdentities();
|
await loadIdentities();
|
||||||
|
|
||||||
// Proceed with disconnect
|
if (addPasswordMode === 'disconnect' && passwordSetupProvider) {
|
||||||
if (passwordSetupProvider) {
|
toast({
|
||||||
|
title: "Password Set",
|
||||||
|
description: "You can now disconnect your social login."
|
||||||
|
});
|
||||||
await handleUnlinkSocial(passwordSetupProvider);
|
await handleUnlinkSocial(passwordSetupProvider);
|
||||||
setPasswordSetupProvider(null);
|
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
|
// Get connected accounts with identity data
|
||||||
const connectedAccounts = [
|
const connectedAccounts = [
|
||||||
{
|
{
|
||||||
@@ -150,27 +176,38 @@ export function SecurityTab() {
|
|||||||
onOpenChange={(open) => !open && setPasswordSetupProvider(null)}
|
onOpenChange={(open) => !open && setPasswordSetupProvider(null)}
|
||||||
onSuccess={handlePasswordSetupSuccess}
|
onSuccess={handlePasswordSetupSuccess}
|
||||||
provider={passwordSetupProvider}
|
provider={passwordSetupProvider}
|
||||||
|
mode={addPasswordMode}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div className="space-y-8">
|
<div className="space-y-8">
|
||||||
{/* Change Password */}
|
{/* Password Section - Conditional based on auth method */}
|
||||||
<div className="space-y-4">
|
<div className="space-y-4">
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<Key className="w-5 h-5" />
|
<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>
|
</div>
|
||||||
|
|
||||||
<Card>
|
<Card>
|
||||||
<CardHeader>
|
<CardHeader>
|
||||||
<CardDescription>
|
<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>
|
</CardDescription>
|
||||||
</CardHeader>
|
</CardHeader>
|
||||||
<CardContent>
|
<CardContent>
|
||||||
<Button onClick={() => setPasswordDialogOpen(true)}>
|
{hasPassword ? (
|
||||||
Change Password
|
<Button onClick={() => setPasswordDialogOpen(true)}>
|
||||||
</Button>
|
Change Password
|
||||||
|
</Button>
|
||||||
|
) : (
|
||||||
|
<Button onClick={handleAddPassword}>
|
||||||
|
Add Password
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</CardContent>
|
</CardContent>
|
||||||
</Card>
|
</Card>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user