import { useState, useEffect } from 'react'; import PocketBase from 'pocketbase'; import { Input } from "./ui/input"; import { Button } from "./ui/button"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "./ui/card"; import { Eye, EyeOff, Loader2 } from "lucide-react"; import { Label } from "./ui/label"; // import type { RecordModel } from 'pocketbase'; const PUBLIC_POCKETBASE_URL = import.meta.env.PUBLIC_POCKETBASE_URL; const pb = new PocketBase(PUBLIC_POCKETBASE_URL); interface RecordModel { id: string; collectionId: string; collectionName: string; created: string; updated: string; [key: string]: any; } interface UserRecord extends RecordModel { email: string; verified?: boolean; emailVisibility?: boolean; username?: string; name?: string; avatar?: string; provider?: string; providerData?: { [key: string]: { id: string; name?: string; email?: string; avatarUrl?: string; } }; lastPasswordReset?: string; passwordHash?: string; } const PasswordUpdateCard = ({ userId, onLogout }: { userId: string, onLogout: () => void }) => { const [currentPassword, setCurrentPassword] = useState(''); const [newPassword, setNewPassword] = useState(''); const [confirmPassword, setConfirmPassword] = useState(''); const [showCurrentPassword, setShowCurrentPassword] = useState(false); const [showNewPassword, setShowNewPassword] = useState(false); const [showConfirmPassword, setShowConfirmPassword] = useState(false); const [status, setStatus] = useState({ message: '', isError: false }); const [isLoading, setIsLoading] = useState(false); const [isOAuthUser, setIsOAuthUser] = useState(false); useEffect(() => { const checkOAuthStatus = async () => { try { // Get user with all fields (including internal ones) const user = await pb.collection('users').getOne(userId, { fields: '*,provider,verified,passwordHash' }); // New reliable OAuth detection logic: const isOAuthUser = ( // Case 1: No password but verified (typical OAuth pattern) (!user.passwordHash && user.verified) || // Case 2: Has provider specified (older OAuth users) (user.provider && user.provider !== 'email') || // Case 3: Check externalId field if exists (some OAuth implementations) (user.externalId && typeof user.externalId === 'string') ); console.log('OAuth determination:', { email: user.email, hasPassword: !!user.passwordHash, verified: user.verified, provider: user.provider, isOAuthUser }); setIsOAuthUser(isOAuthUser); } catch (error) { console.error("Failed to fetch user data:", error); // Fallback to checking verification status setIsOAuthUser(pb.authStore.model?.verified === true); } }; checkOAuthStatus(); }, [userId]); const handleSubmit = async (e: React.FormEvent) => { e.preventDefault(); setIsLoading(true); setStatus({ message: '', isError: false }); try { // Validate inputs if (newPassword !== confirmPassword) { throw new Error("New passwords don't match"); } if (newPassword.length < 8) { throw new Error("Password must be at least 8 characters"); } // SPECIAL HANDLING FOR OAUTH USERS if (isOAuthUser) { // For OAuth users, we need to use a different endpoint or method // since the regular update requires oldPassword const authData = await pb.collection('users').update(userId, { password: newPassword, passwordConfirm: confirmPassword }, { // This header might be needed depending on your PocketBase version headers: { 'X-Require-Password-Confirm': 'false' } }); } else { // Regular password update flow await pb.collection('users').update(userId, { password: newPassword, passwordConfirm: confirmPassword, oldPassword: currentPassword }); } // Clear form setCurrentPassword(''); setNewPassword(''); setConfirmPassword(''); setStatus({ message: isOAuthUser ? 'Password successfully set! You can now login with email' : 'Password updated successfully!', isError: false }); // Logout user after password change onLogout(); } catch (error: any) { console.error("Password update failed:", error); // Special handling for OAuth users if the first method fails if (isOAuthUser && error.data?.oldPassword?.message) { try { // Alternative method for OAuth users - create a new auth record await pb.collection('users').authWithPassword( pb.authStore.model?.email, newPassword ); setStatus({ message: 'Password successfully set!', isError: false }); onLogout(); return; } catch (secondError) { error = secondError; } } // Handle specific PocketBase validation errors let errorMessage = error.message; if (error.data?.oldPassword?.message) { errorMessage = "Please provide your current password"; } else if (error.data?.password?.message) { errorMessage = error.data.password.message; } setStatus({ message: errorMessage || "Failed to update password. Please try again.", isError: true }); } finally { setIsLoading(false); } }; return ( Security {isOAuthUser ? "Set a password to enable email login" : "Update your password"}
{status.message && (
{status.message}
)} {!isOAuthUser && (
setCurrentPassword(e.target.value)} required={!isOAuthUser} />
)}
setNewPassword(e.target.value)} required minLength={8} />

Password must be at least 8 characters

setConfirmPassword(e.target.value)} required minLength={8} />
); }; export default PasswordUpdateCard;