Prakhar Singh commited on
Commit
c1209d2
·
1 Parent(s): 41c4190

Chore:Auth endpoint is integrated in the frontend

Browse files
Backend/app/api/v1/endpoints/auth.py CHANGED
@@ -64,7 +64,7 @@ async def login(request: LoginRequest, db: AsyncSession = Depends(get_db)):
64
  expires_deltas=access_token_expires
65
  )
66
 
67
- return {"access_token":access_token, "token_type":"bearer"}
68
  except HTTPException:
69
  raise
70
  except Exception as e:
 
64
  expires_deltas=access_token_expires
65
  )
66
 
67
+ return {"access_token":access_token, "token_type":"bearer","username":user.username}
68
  except HTTPException:
69
  raise
70
  except Exception as e:
Backend/app/schema/models.py CHANGED
@@ -45,3 +45,5 @@ class Token(BaseModel):
45
  access_token: str
46
  token_type: str
47
 
 
 
 
45
  access_token: str
46
  token_type: str
47
 
48
+ class LoginResponse(Token):
49
+ username: str
Frontend/src/App.tsx CHANGED
@@ -12,11 +12,13 @@ import { AuthProvider, useAuth } from "./components/context/AuthContext";
12
  import ProtectedRoute from "./routes/ProtectedRoute";
13
 
14
  const DashboardLayout = () => {
15
- const { logout } = useAuth();
 
16
 
17
  return (
18
  <div className="flex h-screen bg-gray-100">
19
- <Sidebar onLogout={logout} />
 
20
 
21
  <main className="flex-1 overflow-y-auto">
22
  <Routes>
@@ -36,7 +38,6 @@ const App: React.FC = () => {
36
  <AuthProvider>
37
  <Router>
38
  <Routes>
39
-
40
  {/* Public Home */}
41
  <Route path="/" element={<HomeWrapper />} />
42
 
 
12
  import ProtectedRoute from "./routes/ProtectedRoute";
13
 
14
  const DashboardLayout = () => {
15
+ // 1. Retrieve both logout and username from the AuthContext
16
+ const { logout, username } = useAuth();
17
 
18
  return (
19
  <div className="flex h-screen bg-gray-100">
20
+ {/* 2. Pass the retrieved username prop to the Sidebar */}
21
+ <Sidebar onLogout={logout} username={username} />
22
 
23
  <main className="flex-1 overflow-y-auto">
24
  <Routes>
 
38
  <AuthProvider>
39
  <Router>
40
  <Routes>
 
41
  {/* Public Home */}
42
  <Route path="/" element={<HomeWrapper />} />
43
 
Frontend/src/components/auth/SignIn.tsx CHANGED
@@ -14,7 +14,6 @@ const SignIn: React.FC<SignInProps> = ({
14
  onAuthSuccess,
15
  }) => {
16
  const [email, setEmail] = useState("");
17
- // const [username, setUserName] = useState("");
18
  const [password, setPassword] = useState("");
19
  const [error, setError] = useState("");
20
 
@@ -23,15 +22,37 @@ const SignIn: React.FC<SignInProps> = ({
23
  setError("");
24
 
25
  try {
 
26
  const res = await API.post("/auth/login", {
27
- email: email, // 🔥 FastAPI expects `username` (OAuth2PasswordRequestForm)
28
  password,
29
  });
30
 
31
  localStorage.setItem("token", res.data.access_token);
32
  onAuthSuccess();
33
  } catch (err: any) {
34
- setError(err.response?.data?.detail || "Login failed");
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
35
  }
36
  };
37
 
@@ -41,6 +62,7 @@ const SignIn: React.FC<SignInProps> = ({
41
  Welcome Back
42
  </h2>
43
 
 
44
  {error && <p className="text-red-400 text-center mb-3">{error}</p>}
45
 
46
  <form className="space-y-5" onSubmit={handleSubmit}>
@@ -64,7 +86,7 @@ const SignIn: React.FC<SignInProps> = ({
64
  <div className="relative">
65
  <Lock className="w-5 h-5 absolute left-3 top-1/2 -translate-y-1/2 text-blue-400" />
66
  <input
67
- type="string"
68
  placeholder="••••••••"
69
  value={password}
70
  onChange={(e) => setPassword(e.target.value)}
@@ -74,7 +96,9 @@ const SignIn: React.FC<SignInProps> = ({
74
  </div>
75
  </div>
76
 
77
- <button className="w-full px-5 py-3 rounded-lg bg-linear-to-r from-blue-500 to-gray-500 text-white flex items-center justify-center gap-2">
 
 
78
  <LogIn className="w-5 h-5" />
79
  Sign In
80
  </button>
@@ -90,4 +114,4 @@ const SignIn: React.FC<SignInProps> = ({
90
  );
91
  };
92
 
93
- export default SignIn;
 
14
  onAuthSuccess,
15
  }) => {
16
  const [email, setEmail] = useState("");
 
17
  const [password, setPassword] = useState("");
18
  const [error, setError] = useState("");
19
 
 
22
  setError("");
23
 
24
  try {
25
+ // 1. Send JSON body with keys matching the backend's LoginRequest schema
26
  const res = await API.post("/auth/login", {
27
+ email: email, // Backend expects 'email' key in the JSON body
28
  password,
29
  });
30
 
31
  localStorage.setItem("token", res.data.access_token);
32
  onAuthSuccess();
33
  } catch (err: any) {
34
+ console.error("Login error:", err);
35
+ let errorMessage = "Login failed due to an unknown error.";
36
+
37
+ // --- Robust Error Handling for 422 (Pydantic validation) ---
38
+ if (err.response && err.response.data && err.response.data.detail) {
39
+ const detail = err.response.data.detail;
40
+
41
+ if (typeof detail === 'string') {
42
+ // Handle simple error messages like "Incorrect username or password" (from 401)
43
+ errorMessage = detail;
44
+ } else if (Array.isArray(detail) && detail.length > 0) {
45
+ // Handle 422 Pydantic validation error (list of error objects)
46
+ // We extract the human-readable message from the first object
47
+ const firstError = detail[0];
48
+ errorMessage = `Validation Error on '${firstError.loc.join(' -> ')}': ${firstError.msg}`;
49
+ }
50
+ } else if (err.message) {
51
+ // Fallback for network errors (e.g., server offline)
52
+ errorMessage = err.message;
53
+ }
54
+
55
+ setError(errorMessage);
56
  }
57
  };
58
 
 
62
  Welcome Back
63
  </h2>
64
 
65
+ {/* 2. This ensures only a string is rendered, fixing the React error */}
66
  {error && <p className="text-red-400 text-center mb-3">{error}</p>}
67
 
68
  <form className="space-y-5" onSubmit={handleSubmit}>
 
86
  <div className="relative">
87
  <Lock className="w-5 h-5 absolute left-3 top-1/2 -translate-y-1/2 text-blue-400" />
88
  <input
89
+ type="password" // Changed type to 'password' for browser security
90
  placeholder="••••••••"
91
  value={password}
92
  onChange={(e) => setPassword(e.target.value)}
 
96
  </div>
97
  </div>
98
 
99
+ <button
100
+ type="submit" // Added type="submit" for proper form submission
101
+ className="w-full px-5 py-3 rounded-lg bg-linear-to-r from-blue-500 to-gray-500 text-white flex items-center justify-center gap-2">
102
  <LogIn className="w-5 h-5" />
103
  Sign In
104
  </button>
 
114
  );
115
  };
116
 
117
+ export default SignIn;
Frontend/src/components/auth/SignUp.tsx CHANGED
@@ -1,15 +1,15 @@
1
  import React, { useState } from "react";
2
- import { User, Mail, Lock, UserPlus, Chrome } from "lucide-react";
3
- import API from "../../api/api";
4
 
5
  interface SignUpProps {
6
  onClose: () => void;
7
  onSwitchToSignIn: () => void;
8
- onAuthSuccess: () => void;
9
  }
10
 
11
  const SignUp: React.FC<SignUpProps> = ({ onSwitchToSignIn, onAuthSuccess }) => {
12
- const [fullName, setFullName] = useState("");
13
  const [email, setEmail] = useState("");
14
  const [password, setPassword] = useState("");
15
  const [confirm, setConfirm] = useState("");
@@ -24,21 +24,38 @@ const SignUp: React.FC<SignUpProps> = ({ onSwitchToSignIn, onAuthSuccess }) => {
24
  }
25
 
26
  try {
 
27
  await API.post("/auth/register", {
28
- full_name: fullName,
29
  email,
30
  password,
31
  });
32
 
33
- const loginRes = await API.post("/auth/login", {
34
- username: email,
 
35
  password,
36
  });
37
 
38
- localStorage.setItem("token", loginRes.data.access_token);
39
- onAuthSuccess();
 
 
 
40
  } catch (err: any) {
41
- setError(err.response?.data?.detail || "Registration failed");
 
 
 
 
 
 
 
 
 
 
 
 
42
  }
43
  };
44
 
@@ -50,14 +67,14 @@ const SignUp: React.FC<SignUpProps> = ({ onSwitchToSignIn, onAuthSuccess }) => {
50
 
51
  {error && <p className="text-red-400 text-center mb-3">{error}</p>}
52
 
53
- <form className="space-y-5" onSubmit={handleSubmit}>
54
  <div>
55
- <label className="text-gray-300">Full Name</label>
56
  <div className="relative">
57
  <User className="w-5 h-5 absolute left-3 top-1/2 -translate-y-1/2 text-blue-400" />
58
  <input
59
- value={fullName}
60
- onChange={(e) => setFullName(e.target.value)}
61
  type="text"
62
  className="w-full pl-10 py-3 bg-slate-800 border border-slate-700 text-white rounded-lg"
63
  required
@@ -107,15 +124,18 @@ const SignUp: React.FC<SignUpProps> = ({ onSwitchToSignIn, onAuthSuccess }) => {
107
  </div>
108
  </div>
109
 
110
- <button className="w-full px-5 py-3 rounded-lg bg-linear-to-r from-blue-500 to-gray-500 text-white flex items-center justify-center gap-2">
 
 
 
111
  <UserPlus className="w-5 h-5" />
112
  Sign Up
113
  </button>
114
- </form>
115
 
116
  <p className="mt-8 text-center text-sm text-gray-400">
117
  Already have an account?
118
- <button onClick={onSwitchToSignIn} className="text-blue-400 ml-2">
119
  Sign In
120
  </button>
121
  </p>
 
1
  import React, { useState } from "react";
2
+ import { User, Mail, Lock, UserPlus } from "lucide-react";
3
+ import API from "../../api/api"; // Assuming the API file is one directory up
4
 
5
  interface SignUpProps {
6
  onClose: () => void;
7
  onSwitchToSignIn: () => void;
8
+ onAuthSuccess: (username: string) => void;
9
  }
10
 
11
  const SignUp: React.FC<SignUpProps> = ({ onSwitchToSignIn, onAuthSuccess }) => {
12
+ const [username, setUsername] = useState("");
13
  const [email, setEmail] = useState("");
14
  const [password, setPassword] = useState("");
15
  const [confirm, setConfirm] = useState("");
 
24
  }
25
 
26
  try {
27
+ // 1. Registration via API (uses axios and the correct base URL)
28
  await API.post("/auth/register", {
29
+ username,
30
  email,
31
  password,
32
  });
33
 
34
+ // 2. Login immediately after successful registration
35
+ const loginResponse = await API.post("/auth/login", {
36
+ email,
37
  password,
38
  });
39
 
40
+ // The response data is directly on response.data when using axios
41
+ const loginData = loginResponse.data;
42
+ localStorage.setItem("token", loginData.access_token);
43
+ const loggedInUsername = loginData.username;
44
+ onAuthSuccess(loggedInUsername);
45
  } catch (err: any) {
46
+ console.error("Registration/Login error:", err);
47
+
48
+ // Axios error handling: Check for response data and detail for custom error message
49
+ let errorMessage = "Registration failed";
50
+ if (err.response && err.response.data && err.response.data.detail) {
51
+ // Extracts the specific error detail from FastAPI (e.g., 'Username already registered')
52
+ errorMessage = err.response.data.detail;
53
+ } else if (err.message) {
54
+ // Use generic network or request error message
55
+ errorMessage = err.message;
56
+ }
57
+
58
+ setError(errorMessage);
59
  }
60
  };
61
 
 
67
 
68
  {error && <p className="text-red-400 text-center mb-3">{error}</p>}
69
 
70
+ <div className="space-y-5">
71
  <div>
72
+ <label className="text-gray-300">Username</label>
73
  <div className="relative">
74
  <User className="w-5 h-5 absolute left-3 top-1/2 -translate-y-1/2 text-blue-400" />
75
  <input
76
+ value={username}
77
+ onChange={(e) => setUsername(e.target.value)}
78
  type="text"
79
  className="w-full pl-10 py-3 bg-slate-800 border border-slate-700 text-white rounded-lg"
80
  required
 
124
  </div>
125
  </div>
126
 
127
+ <button
128
+ onClick={handleSubmit}
129
+ className="w-full px-5 py-3 rounded-lg bg-gradient-to-r from-blue-500 to-purple-500 text-white flex items-center justify-center gap-2 hover:from-blue-600 hover:to-purple-600 transition"
130
+ >
131
  <UserPlus className="w-5 h-5" />
132
  Sign Up
133
  </button>
134
+ </div>
135
 
136
  <p className="mt-8 text-center text-sm text-gray-400">
137
  Already have an account?
138
+ <button onClick={onSwitchToSignIn} className="text-blue-400 ml-2 hover:underline">
139
  Sign In
140
  </button>
141
  </p>
Frontend/src/components/context/AuthContext.tsx CHANGED
@@ -2,7 +2,10 @@ import React, { createContext, useContext, useState } from "react";
2
 
3
  interface AuthContextType {
4
  isAuthenticated: boolean;
5
- login: () => void;
 
 
 
6
  logout: () => void;
7
  }
8
 
@@ -10,12 +13,23 @@ const AuthContext = createContext<AuthContextType | undefined>(undefined);
10
 
11
  export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
12
  const [isAuthenticated, setIsAuthenticated] = useState(false);
 
 
13
 
14
- const login = () => setIsAuthenticated(true);
15
- const logout = () => setIsAuthenticated(false);
 
 
 
 
 
 
 
 
 
16
 
17
  return (
18
- <AuthContext.Provider value={{ isAuthenticated, login, logout }}>
19
  {children}
20
  </AuthContext.Provider>
21
  );
@@ -25,4 +39,4 @@ export const useAuth = () => {
25
  const ctx = useContext(AuthContext);
26
  if (!ctx) throw new Error("useAuth must be inside AuthProvider");
27
  return ctx;
28
- };
 
2
 
3
  interface AuthContextType {
4
  isAuthenticated: boolean;
5
+ // 1. Added username to the context type
6
+ username: string;
7
+ // 2. Updated login signature to accept the username string
8
+ login: (name: string) => void;
9
  logout: () => void;
10
  }
11
 
 
13
 
14
  export const AuthProvider = ({ children }: { children: React.ReactNode }) => {
15
  const [isAuthenticated, setIsAuthenticated] = useState(false);
16
+ // 3. New state to hold the logged-in user's name
17
+ const [username, setUsername] = useState('Guest');
18
 
19
+ // 4. Updated login function to accept and set the username
20
+ const login = (name: string) => {
21
+ setUsername(name);
22
+ setIsAuthenticated(true);
23
+ };
24
+
25
+ const logout = () => {
26
+ // Reset state on logout
27
+ setIsAuthenticated(false);
28
+ setUsername('Guest');
29
+ };
30
 
31
  return (
32
+ <AuthContext.Provider value={{ isAuthenticated, username, login, logout }}>
33
  {children}
34
  </AuthContext.Provider>
35
  );
 
39
  const ctx = useContext(AuthContext);
40
  if (!ctx) throw new Error("useAuth must be inside AuthProvider");
41
  return ctx;
42
+ };
Frontend/src/components/dashboard/Sidebar.tsx CHANGED
@@ -1,15 +1,16 @@
1
  import React from "react";
2
  import { Link, useLocation } from "react-router-dom";
3
- import {
4
- Home, FileText, Brain, BookOpen,
5
- Settings, User, LogOut
6
  } from "lucide-react";
7
 
8
  interface SidebarProps {
9
- onLogout?: () => void; // Function to handle logging out
 
10
  }
11
 
12
- const Sidebar: React.FC<SidebarProps> = ({ onLogout }) => {
13
  const location = useLocation();
14
 
15
  const navItems = [
@@ -18,10 +19,10 @@ const Sidebar: React.FC<SidebarProps> = ({ onLogout }) => {
18
  { path: "/AIInterview", label: "AI Interview", icon: <Brain size={18} /> },
19
  { path: "/quize", label: "Resume Quiz", icon: <FileText size={18} /> },
20
  ];
21
-
22
  return (
23
  <aside className="w-64 bg-linear-to-r from-blue-700 to-gray-900/50 text-white flex flex-col justify-between">
24
-
25
  {/* Top Section */}
26
  <div>
27
  <div className="text-2xl font-bold text-center py-6 border-b border-gray-700">
@@ -33,9 +34,8 @@ const Sidebar: React.FC<SidebarProps> = ({ onLogout }) => {
33
  <Link
34
  key={item.path}
35
  to={item.path}
36
- className={`flex items-center space-x-3 p-3 rounded-lg hover:bg-gray-800 transition ${
37
- location.pathname === item.path ? "bg-gray-800" : ""
38
- }`}
39
  >
40
  {item.icon}
41
  <span>{item.label}</span>
@@ -52,14 +52,14 @@ const Sidebar: React.FC<SidebarProps> = ({ onLogout }) => {
52
  <span>Settings</span>
53
  </button>
54
 
55
- {/* User Profile Placeholder */}
56
  <button className="flex items-center space-x-3 p-3 rounded-lg hover:bg-gray-800 transition w-full text-left text-gray-300">
57
  <User size={18} />
58
- <span>John Doe</span>
59
  </button>
60
 
61
  {/* Logout Button (Click handler added here) */}
62
- <button
63
  onClick={onLogout}
64
  className="flex items-center space-x-3 p-3 rounded-lg bg-red-500 hover:bg-red-600 text-black hover:text-white transition w-full text-left font-medium"
65
  >
 
1
  import React from "react";
2
  import { Link, useLocation } from "react-router-dom";
3
+ import {
4
+ Home, FileText, Brain, BookOpen,
5
+ Settings, User, LogOut
6
  } from "lucide-react";
7
 
8
  interface SidebarProps {
9
+ onLogout?: () => void; // Function to handle logging out
10
+ username: string;
11
  }
12
 
13
+ const Sidebar: React.FC<SidebarProps> = ({ onLogout, username }) => {
14
  const location = useLocation();
15
 
16
  const navItems = [
 
19
  { path: "/AIInterview", label: "AI Interview", icon: <Brain size={18} /> },
20
  { path: "/quize", label: "Resume Quiz", icon: <FileText size={18} /> },
21
  ];
22
+
23
  return (
24
  <aside className="w-64 bg-linear-to-r from-blue-700 to-gray-900/50 text-white flex flex-col justify-between">
25
+
26
  {/* Top Section */}
27
  <div>
28
  <div className="text-2xl font-bold text-center py-6 border-b border-gray-700">
 
34
  <Link
35
  key={item.path}
36
  to={item.path}
37
+ className={`flex items-center space-x-3 p-3 rounded-lg hover:bg-gray-800 transition ${location.pathname === item.path ? "bg-gray-800" : ""
38
+ }`}
 
39
  >
40
  {item.icon}
41
  <span>{item.label}</span>
 
52
  <span>Settings</span>
53
  </button>
54
 
55
+ {/* User Profile Button - Now displays the actual username */}
56
  <button className="flex items-center space-x-3 p-3 rounded-lg hover:bg-gray-800 transition w-full text-left text-gray-300">
57
  <User size={18} />
58
+ <span>{username || "Guest User"}</span>
59
  </button>
60
 
61
  {/* Logout Button (Click handler added here) */}
62
+ <button
63
  onClick={onLogout}
64
  className="flex items-center space-x-3 p-3 rounded-lg bg-red-500 hover:bg-red-600 text-black hover:text-white transition w-full text-left font-medium"
65
  >
Frontend/src/pages/home.tsx CHANGED
@@ -20,7 +20,7 @@ interface Step {
20
 
21
  // Add onLogin prop
22
  interface AIInterviewPlatformProps {
23
- onLogin: () => void;
24
  }
25
 
26
  const AIInterviewPlatform: React.FC<AIInterviewPlatformProps> = ({ onLogin }) => {
@@ -42,9 +42,9 @@ const AIInterviewPlatform: React.FC<AIInterviewPlatformProps> = ({ onLogin }) =>
42
  const closeModal = () => setModalType('none');
43
 
44
  // Handle successful authentication
45
- const handleAuthSuccess = () => {
46
  closeModal();
47
- onLogin(); // Call the parent function to update auth state
48
  };
49
 
50
  useEffect(() => {
 
20
 
21
  // Add onLogin prop
22
  interface AIInterviewPlatformProps {
23
+ onLogin: (username:string) => void;
24
  }
25
 
26
  const AIInterviewPlatform: React.FC<AIInterviewPlatformProps> = ({ onLogin }) => {
 
42
  const closeModal = () => setModalType('none');
43
 
44
  // Handle successful authentication
45
+ const handleAuthSuccess = (username:string) => {
46
  closeModal();
47
+ onLogin(username); // Call the parent function to update auth state
48
  };
49
 
50
  useEffect(() => {