fix(auth): resolve demo login redirect loop, fix backend API bugs, restore clean login UI, and add comprehensive API docs with headers

This commit is contained in:
2026-09-04 11:49:13 +05:30
parent b152f73c3d
commit 3ad281aae6
8 changed files with 1299 additions and 52 deletions

View File

@@ -20,7 +20,7 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
const [dropdownOpen, setDropdownOpen] = useState(false)
const dropdownRef = useRef<HTMLDivElement>(null)
const { userId, user, globalSettings, switchUser, hasAccessTo, loading } = useActiveUser();
const { userId, user, globalSettings, switchUser, logout, hasAccessTo, loading } = useActiveUser();
const [maintenance, setMaintenance] = useState(false);
const [checkingMaintenance, setCheckingMaintenance] = useState(true);
@@ -264,6 +264,13 @@ export default function DashboardLayout({ children }: { children: ReactNode }) {
>
Reset Demo
</button>
<div className="h-px bg-border my-1" />
<button
onClick={logout}
className="w-full text-left flex items-center gap-2 px-3 py-2 text-sm text-red-500 hover:bg-red-500/10 rounded-lg transition-colors font-semibold"
>
Sign Out
</button>
</div>
</div>
)}

View File

@@ -30,22 +30,32 @@ export default function LoginPage() {
if (data.success) {
if (typeof window !== "undefined") {
localStorage.setItem("activeUserId", data.token)
// Set auth_token cookie required by Next.js 16 proxy.ts middleware
const cookieVal = data.role === "admin" ? "admin" : (data.token || "authenticated");
document.cookie = `auth_token=${cookieVal}; path=/; max-age=2592000; SameSite=Lax`;
localStorage.setItem("activeUserId", data.token);
// Create new session token for single-device login
const sessionToken = crypto.randomUUID();
localStorage.setItem("active_session_token", sessionToken);
fetch(`/api/users/${data.token}/session`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session_token: sessionToken })
}).finally(() => {
if (data.role === "admin") {
window.location.href = "/admin";
} else {
window.location.href = "/dashboard";
if (data.token && data.token !== "admin") {
try {
await fetch(`/api/users/${data.token}/session`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ session_token: sessionToken })
});
} catch (e) {
console.warn("Session update skipped", e);
}
});
}
if (data.role === "admin") {
window.location.href = "/admin";
} else {
window.location.href = "/dashboard";
}
}
} else {
setError(data.error || "Invalid email or password.")
@@ -54,6 +64,7 @@ export default function LoginPage() {
setError("An error occurred. Please try again.")
}
}
return (
<div className="min-h-screen bg-background flex flex-col items-center justify-center p-6 selection:bg-[#00F0FF]/20">
<Link href="/" className="absolute top-8 left-8 text-muted-foreground hover:text-foreground flex items-center gap-2 transition-colors font-medium">

View File

@@ -46,6 +46,7 @@ export default function SignupPage() {
const loginData = await loginRes.json();
if (loginData.success && typeof window !== "undefined") {
document.cookie = `auth_token=${loginData.token}; path=/; max-age=2592000; SameSite=Lax`;
localStorage.setItem("activeUserId", loginData.token);
const sessionToken = crypto.randomUUID();
localStorage.setItem("active_session_token", sessionToken);

View File

@@ -123,6 +123,7 @@ export function useActiveUser() {
const switchUser = useCallback(async (newId: string) => {
if (typeof window !== "undefined") {
document.cookie = `auth_token=${newId}; path=/; max-age=2592000; SameSite=Lax`;
const sessionToken = crypto.randomUUID();
localStorage.setItem("active_session_token", sessionToken);
@@ -142,6 +143,15 @@ export function useActiveUser() {
}
}, []);
const logout = useCallback(() => {
if (typeof window !== "undefined") {
document.cookie = "auth_token=; path=/; expires=Thu, 01 Jan 1970 00:00:00 GMT";
localStorage.removeItem("activeUserId");
localStorage.removeItem("active_session_token");
window.location.href = "/login";
}
}, []);
const hasAccessTo = useCallback((feature: string): boolean => {
if (!user) return false;
let tier = user.tier;
@@ -178,14 +188,11 @@ export function useActiveUser() {
case "pdf-export":
return tier === "Pro" || tier === "Al_Coach";
case "ai-mistake-tracking":
case "spaced-repetition":
case "ai-coach-custom-schedules":
case "personalized-study-plan":
case "ai-tutor-chat":
case "exam-readiness-score":
case "exam-day-strategy":
case "college-predictor":
case "study-time-optimization":
case "ai-tutor-doubt-resolution":
case "ai-mistake-tracking":
case "ai-readiness-prediction":
return tier === "Al_Coach";
default:
@@ -201,6 +208,7 @@ export function useActiveUser() {
loading,
error,
switchUser,
logout,
hasAccessTo,
refreshUser: async () => {
await fetchUserProfile(userId);