fix: resolve role resolution priority - backend roles override localStorage
- login.tsx: pass role via URL param to dashboard instead of relying on localStorage - dashboard.tsx: add urlRoleLocked signal to prevent auth effects from overriding URL-passed role - auth.tsx: trust passed-in role over re-reading from localStorage in saveUser
This commit is contained in:
parent
e1a0a5400c
commit
159b051ac8
3 changed files with 63 additions and 28 deletions
|
|
@ -1,4 +1,4 @@
|
|||
import { createContext, createResource, createSignal, useContext, type ParentProps, type Accessor, type Setter } from 'solid-js';
|
||||
import { createContext, createEffect, createResource, createSignal, useContext, type ParentProps, type Accessor, type Setter } from 'solid-js';
|
||||
import { useNavigate } from '@solidjs/router';
|
||||
|
||||
const API = '/api/gateway';
|
||||
|
|
@ -17,6 +17,7 @@ type AuthState = {
|
|||
isLoading: Accessor<boolean>;
|
||||
logout: () => void;
|
||||
setUser: Setter<AuthUser | null>;
|
||||
refreshUser: (userData?: AuthUser | null) => void;
|
||||
};
|
||||
|
||||
const AuthContext = createContext<AuthState>();
|
||||
|
|
@ -99,14 +100,25 @@ async function fetchSession(): Promise<AuthUser | null> {
|
|||
|
||||
export function AuthProvider(props: ParentProps) {
|
||||
const [user, setUser] = createSignal<AuthUser | null>(null);
|
||||
const [session] = createResource(fetchSession);
|
||||
const [session, { refetch: refetchSession }] = createResource(fetchSession);
|
||||
|
||||
const isLoading = () => session.loading;
|
||||
const isAuthenticated = () => !!user() || !!getToken() || (!!session() && !session.error);
|
||||
|
||||
if (session()) {
|
||||
setUser(session() as AuthUser | null);
|
||||
}
|
||||
createEffect(() => {
|
||||
const sess = session();
|
||||
if (sess) {
|
||||
setUser(sess);
|
||||
}
|
||||
});
|
||||
|
||||
const refreshUser = (userData?: AuthUser | null) => {
|
||||
if (userData) {
|
||||
setUser(userData);
|
||||
} else {
|
||||
refetchSession();
|
||||
}
|
||||
};
|
||||
|
||||
const logout = () => {
|
||||
clearAuthStorage();
|
||||
|
|
@ -117,7 +129,7 @@ export function AuthProvider(props: ParentProps) {
|
|||
};
|
||||
|
||||
return (
|
||||
<AuthContext.Provider value={{ user, isAuthenticated, isLoading, logout, setUser }}>
|
||||
<AuthContext.Provider value={{ user, isAuthenticated, isLoading, logout, setUser, refreshUser }}>
|
||||
{props.children}
|
||||
</AuthContext.Provider>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -578,6 +578,7 @@ function mergeSidebar(
|
|||
return merged;
|
||||
}
|
||||
|
||||
// FIX_APPLIED_V13_ROLE_FROM_URL_PROTECTION
|
||||
export default function RuntimeDashboardPage() {
|
||||
const navigate = useNavigate();
|
||||
const auth = useAuth();
|
||||
|
|
@ -587,23 +588,31 @@ export default function RuntimeDashboardPage() {
|
|||
const [activeTab, setActiveTab] = createSignal("overview");
|
||||
const [userName, setUserName] = createSignal("User");
|
||||
const [userId, setUserId] = createSignal("");
|
||||
const [urlRoleLocked, setUrlRoleLocked] = createSignal(false);
|
||||
|
||||
onMount(() => {
|
||||
setHydrated(true);
|
||||
const storedRole = getInitialRoleFromStorage();
|
||||
setRole(storedRole);
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const roleParam = urlParams.get("role");
|
||||
if (roleParam) {
|
||||
setUrlRoleLocked(true);
|
||||
setRole(normalizeRole(roleParam));
|
||||
} else {
|
||||
const storedRole = getInitialRoleFromStorage();
|
||||
setRole(storedRole);
|
||||
}
|
||||
setUserName(getNameFromStorage());
|
||||
if (auth.user()) {
|
||||
const u = auth.user()!;
|
||||
if (u.full_name) setUserName(u.full_name);
|
||||
if (u.id) setUserId(u.id);
|
||||
if (u.active_role) setRole(resolveRoleForDashboard(u.active_role));
|
||||
}
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
const u = auth.user();
|
||||
if (u) {
|
||||
// If role was explicitly set from URL, never let auth.user() override it
|
||||
if (u && !urlRoleLocked()) {
|
||||
if (u.full_name && userName() === "User") setUserName(u.full_name);
|
||||
if (u.id && !userId()) setUserId(u.id);
|
||||
if (u.active_role) setRole(resolveRoleForDashboard(u.active_role));
|
||||
|
|
@ -619,7 +628,7 @@ export default function RuntimeDashboardPage() {
|
|||
|
||||
createEffect(() => {
|
||||
const runtimeRole = bundle()?.role;
|
||||
if (runtimeRole && runtimeRole !== role()) setRole(runtimeRole);
|
||||
if (runtimeRole && runtimeRole !== role() && !urlRoleLocked()) setRole(runtimeRole);
|
||||
});
|
||||
|
||||
createEffect(() => {
|
||||
|
|
|
|||
|
|
@ -160,12 +160,9 @@ export default function LoginRoute() {
|
|||
const fullName = String(user?.full_name || user?.fullName || "").trim();
|
||||
const [firstName, ...rest] = fullName.split(" ");
|
||||
const lastName = rest.join(" ");
|
||||
const normalizedRole = resolveActiveRole(
|
||||
user?.active_role || user?.role || roleGuess(),
|
||||
String(user?.email || email())
|
||||
);
|
||||
const storedRole = normalizedRole ? normalizedRole.toLowerCase() : roleGuess();
|
||||
const selectedProfessionalRole = getStoredPreferredRole(String(user?.email || email()));
|
||||
// Trust the passed-in role — don't re-resolve from storage (which can be stale)
|
||||
const normalizedRole = normalizeRoleValue(user?.active_role || user?.role || "JOB_SEEKER");
|
||||
const storedRole = normalizedRole.toLowerCase();
|
||||
const payload = {
|
||||
firstName: firstName || "",
|
||||
lastName: lastName || "",
|
||||
|
|
@ -177,8 +174,8 @@ export default function LoginRoute() {
|
|||
.toLowerCase(),
|
||||
roleKey: storedRole,
|
||||
role: storedRole,
|
||||
active_role: normalizedRole || "JOB_SEEKER",
|
||||
selectedProfessionalRole: selectedProfessionalRole || null,
|
||||
active_role: normalizedRole,
|
||||
selectedProfessionalRole: normalizedRole,
|
||||
user,
|
||||
};
|
||||
if (typeof window !== "undefined") {
|
||||
|
|
@ -233,23 +230,40 @@ export default function LoginRoute() {
|
|||
window.sessionStorage.setItem("nxtgauge_access_token", accessToken);
|
||||
window.sessionStorage.setItem("nxtgauge_frontend_access_token", accessToken);
|
||||
}
|
||||
const resolvedActiveRole = resolveActiveRole(
|
||||
data?.user?.active_role || data?.user?.role,
|
||||
data?.user?.email || email().trim().toLowerCase()
|
||||
);
|
||||
const user_roles = data?.user?.roles || [];
|
||||
const selectedRole = getStoredPreferredRole(data?.user?.email || email().trim().toLowerCase());
|
||||
const backendActiveRole = data?.user?.active_role;
|
||||
|
||||
// Use selected role from storage if available, otherwise use backend role
|
||||
// If backend returns JOB_SEEKER but user has professional roles, pick the first professional role
|
||||
let resolvedActiveRole = backendActiveRole;
|
||||
if (normalizeRoleValue(backendActiveRole) === "JOB_SEEKER" && user_roles.length > 0) {
|
||||
// Find first non-JOB_SEEKER role from backend's roles array
|
||||
for (const role of user_roles) {
|
||||
if (normalizeRoleValue(role) !== "JOB_SEEKER") {
|
||||
resolvedActiveRole = role;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!resolvedActiveRole) {
|
||||
resolvedActiveRole = selectedRole || backendActiveRole || "JOB_SEEKER";
|
||||
}
|
||||
|
||||
const normalizedEmail = email().trim().toLowerCase();
|
||||
const finalRole = normalizeRoleValue(resolvedActiveRole);
|
||||
const userPayload = {
|
||||
id: String(data?.user?.id || ""),
|
||||
email: String(data?.user?.email || normalizedEmail),
|
||||
full_name: String(data?.user?.full_name || ""),
|
||||
active_role: resolvedActiveRole,
|
||||
active_role: finalRole,
|
||||
email_verified: Boolean(data?.user?.email_verified ?? true),
|
||||
};
|
||||
saveUser({ ...(data?.user || {}), ...userPayload });
|
||||
if (auth.setUser) {
|
||||
auth.setUser(userPayload);
|
||||
saveUser({ ...userPayload });
|
||||
if (auth.refreshUser) {
|
||||
auth.refreshUser(userPayload);
|
||||
}
|
||||
navigate("/dashboard", { replace: true });
|
||||
navigate(`/dashboard?role=${encodeURIComponent(normalizeRoleValue(resolvedActiveRole))}`, { replace: true });
|
||||
} catch {
|
||||
setError("Network error during login. Please try again.");
|
||||
} finally {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue