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:
@@ -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>
|
||||
)}
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user