Skip to content

Commit 3631e56

Browse files
authored
made password reset flow functional and also added username selection(with uniqueness check) on the signup form (#65)
* feat: improved Supabase password reset flow, username check, and UX enhancements * chore: add explainer comments to password reset, signup, and auth flows * feat: username validation, canonical email, and frontend improvements (no password_hash removal) * chore: update after review or manual change
1 parent 5690da4 commit 3631e56

File tree

7 files changed

+355
-265
lines changed

7 files changed

+355
-265
lines changed

Backend/.env-example

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,4 @@ port=5432
55
dbname=postgres
66
GROQ_API_KEY=
77
SUPABASE_URL=
8-
SUPABASE_KEY=
8+
SUPABASE_KEY=

Backend/app/models/models.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ class User(Base):
2727
id = Column(String, primary_key=True, default=generate_uuid)
2828
username = Column(String, unique=True, nullable=False)
2929
email = Column(String, unique=True, nullable=False)
30-
password_hash = Column(Text, nullable=False)
30+
password_hash = Column(Text, nullable=False) # Restored for now
3131
role = Column(String, nullable=False) # 'creator' or 'brand'
3232
profile_image = Column(Text, nullable=True)
3333
bio = Column(Text, nullable=True)

Backend/app/routes/post.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ async def create_user(user: UserCreate):
4444
"id": user_id,
4545
"username": user.username,
4646
"email": user.email,
47-
"password_hash": user.password_hash,
47+
"password_hash": user.password_hash,
4848
"role": user.role,
4949
"profile_image": user.profile_image,
5050
"bio": user.bio,

Frontend/src/context/AuthContext.tsx

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
ReactNode,
66
useEffect,
77
} from "react";
8-
import { useNavigate } from "react-router-dom";
8+
import { useNavigate, useLocation } from "react-router-dom";
99
import { supabase, User } from "../utils/supabase";
1010

1111
interface AuthContextType {
@@ -25,6 +25,7 @@ export const AuthProvider = ({ children }: AuthProviderProps) => {
2525
const [user, setUser] = useState<User | null>(null);
2626
const [isAuthenticated, setIsAuthenticated] = useState<boolean>(false);
2727
const navigate = useNavigate();
28+
const location = useLocation();
2829

2930
useEffect(() => {
3031
supabase.auth.getSession().then(({ data }) => {
@@ -34,14 +35,20 @@ export const AuthProvider = ({ children }: AuthProviderProps) => {
3435
const { data: listener } = supabase.auth.onAuthStateChange(
3536
(event, session) => {
3637
setUser(session?.user || null);
37-
if (session?.user) {
38+
// Only redirect to dashboard if not on /reset-password and not during password recovery
39+
40+
if (
41+
session?.user &&
42+
location.pathname !== "/reset-password" &&
43+
event !== "PASSWORD_RECOVERY"
44+
) {
3845
navigate("/dashboard");
3946
}
4047
}
4148
);
4249

4350
return () => listener.subscription.unsubscribe();
44-
}, []);
51+
}, [location.pathname, navigate]);
4552

4653
const login = () => {
4754
setIsAuthenticated(true);

Frontend/src/pages/ForgotPassword.tsx

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,44 @@
11
import { useState } from "react";
22
import { Link } from "react-router-dom";
33
import { ArrowLeft, Check, Rocket } from "lucide-react";
4+
import { supabase } from "../utils/supabase";
45

56
export default function ForgotPasswordPage() {
67
const [email, setEmail] = useState("");
78
const [isLoading, setIsLoading] = useState(false);
89
const [isSubmitted, setIsSubmitted] = useState(false);
910
const [error, setError] = useState("");
11+
const [showSignupPrompt, setShowSignupPrompt] = useState(false);
1012

1113
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
1214
e.preventDefault();
1315
setIsLoading(true);
1416
setError("");
17+
setShowSignupPrompt(false);
1518

1619
try {
17-
// In a real app, you would call your auth API here
18-
await new Promise((resolve) => setTimeout(resolve, 1500));
20+
// Check if the email exists in the users table before sending a reset link
21+
const { data: users, error: userError } = await supabase
22+
.from("users")
23+
.select("id")
24+
.eq("email", email)
25+
.maybeSingle();
26+
if (userError) throw userError;
27+
if (!users) {
28+
// If the email does not exist, prompt the user to sign up
29+
setShowSignupPrompt(true);
30+
setIsLoading(false);
31+
return;
32+
}
33+
// Send the password reset email using Supabase Auth
34+
const { error } = await supabase.auth.resetPasswordForEmail(email, {
35+
redirectTo: window.location.origin + "/reset-password"
36+
});
37+
if (error) throw error;
1938
setIsSubmitted(true);
20-
} catch (err) {
21-
setError("Something went wrong. Please try again.");
39+
} catch (err: any) {
40+
41+
setError(err.message || "Something went wrong. Please try again.");
2242
} finally {
2343
setIsLoading(false);
2444
}
@@ -88,6 +108,12 @@ export default function ForgotPasswordPage() {
88108
</div>
89109
)}
90110

111+
{showSignupPrompt && (
112+
<div className="mb-6 p-4 bg-yellow-50 dark:bg-yellow-900/20 border border-yellow-200 dark:border-yellow-800 rounded-lg text-yellow-700 dark:text-yellow-400 text-sm animate-[pulse_1s_ease-in-out]">
113+
No account found with this email. <Link to="/signup" className="underline text-purple-600">Sign up?</Link>
114+
</div>
115+
)}
116+
91117
<form onSubmit={handleSubmit} className="space-y-6">
92118
<div className="space-y-2">
93119
<label
@@ -103,7 +129,8 @@ export default function ForgotPasswordPage() {
103129
onChange={(e) => setEmail(e.target.value)}
104130
required
105131
className="w-full px-4 py-3 rounded-lg border border-gray-300 dark:border-gray-600 focus:outline-none focus:ring-2 focus:ring-purple-500 dark:focus:ring-purple-400 focus:border-transparent bg-white dark:bg-gray-700 text-gray-900 dark:text-white transition-all duration-200"
106-
placeholder="[email protected]"
132+
// Email is case sensitive for password reset
133+
placeholder="[email protected] (CASE sensitive)"
107134
/>
108135
</div>
109136

0 commit comments

Comments
 (0)