import { createContext, useEffect, useState } from 'react';
import * as coreApi from '@services/ohtCore/Endpoint.ts';
import { Permissions, User } from '@services/ohtCore/types.ts';
import {
clearAuthTokens,
getAuthTokens,
setAuthTokensInCookies,
} from '@services/auth/tokenStorage.ts';
import logger from '@utils/logger.ts';
import LoadingSpinner from '@components/LoadingSpinner';
interface AuthContextProps {
user: User | null;
accessToken: string | null;
login: (username: string, password: string) => any;
logout: () => any;
isPermissionPresent: (permission: Permissions) => any;
refreshExpiredToken: () => any;
checkForImpersonationToken: () => any;
fetchUser: () => Promise<User | null>;
}
const defaultAuthContext: AuthContextProps = {
user: null,
accessToken: null,
login: async () => {},
logout: () => {},
isPermissionPresent: () => {},
refreshExpiredToken: () => {},
checkForImpersonationToken: async () => {},
fetchUser: async () => null,
};
const AuthContext = createContext(defaultAuthContext);
export const AuthProvider = ({ children }: { children: any }) => {
const [accessToken, setAccessToken] = useState<string | null>(null);
const [user, setUser] = useState<User | null>(null);
const [loading, setLoading] = useState(true);
useEffect(() => {
// Retrieve the existing token from cookies on page load
console.log('User:', user);
}, [user]);
const checkForImpersonationToken = async () => {
const urlParams = new URLSearchParams(window.location.search);
const impersonationToken = urlParams.get('imp');
if (impersonationToken) {
try {
// Remove the impersonation token from the URL after using it
window.history.replaceState(
{},
document.title,
window.location.pathname
);
const response = await coreApi.RefreshTokens.post({
refreshToken: impersonationToken,
});
const { token, refreshToken, expiresAt } = response;
// Set token in cookies
setAuthTokensInCookies({ token, refreshToken, expiresAt });
setAccessToken(token);
} catch (error) {
logger.error('Failed to impersonate user', error);
setAccessToken(null);
}
} else {
await refreshExpiredToken();
}
};
useEffect(() => {
// Retrieve the existing token from cookies on page load
const { token } = getAuthTokens();
setAccessToken(token);
checkForImpersonationToken().finally(() => setLoading(false));
}, []);
useEffect(() => {
if (accessToken) {
setLoading(true); // Start loading when accessToken is present
fetchUser()
.then(u => {
setUser(u);
})
.finally(() => setLoading(false));
}
}, [accessToken]);
const login = async (username: string, password: string) => {
setLoading(true); // Start loading when logging in
try {
const credentials: any = { username, password };
const response = await coreApi.Tokens.create(credentials);
const { token, refreshToken, expiresAt } = response;
// Set token in cookies
setAuthTokensInCookies({ token, refreshToken, expiresAt });
setAccessToken(token);
} catch (error) {
setAccessToken(null);
setLoading(false); // Stop loading if login fails
throw error;
}
};
const fetchUser = async () => {
if (accessToken) {
return coreApi.Users.setAuthToken(accessToken).get('me');
}
return null;
};
const refreshExpiredToken = async () => {
const { refreshToken, expiresAt } = getAuthTokens();
if (refreshToken && expiresAt && new Date(expiresAt) > new Date()) {
const refreshTokenResponse = await coreApi.RefreshTokens.post({
refreshToken,
});
if (refreshTokenResponse) {
setAuthTokensInCookies(refreshTokenResponse);
setAccessToken(refreshTokenResponse.token);
}
} else {
const { token } = getAuthTokens();
setAccessToken(token);
}
};
const logout = () => {
clearAuthTokens();
setAccessToken(null);
setUser(null);
window.location.replace('/signIn');
};
const isPermissionPresent = (permission: Permissions) => {
return user?.permissions?.includes(permission) ?? false;
};
if (loading) {
return <LoadingSpinner isCentered defaultSpinner size="size-oht-1400" />;
}
return (
<AuthContext.Provider
value={{
user,
accessToken,
login,
logout,
isPermissionPresent,
refreshExpiredToken,
checkForImpersonationToken,
fetchUser,
}}
>
{children}
</AuthContext.Provider>
);
};
export default AuthContext;
I have this authContext on my app and I can’t seem to identify why when I refresh the page I’m getting 3 console.logs of the user 2 with null value and one with the final value of the user and I use the user on another WhiteLabelContext to load partner assets and designTokens. Local it seems to work just fine when I login and when I refresh the page but on stagging the login works but the refresh of the page is showing the children without the partnerAssets and DesignTokens for a moment and then switch I think is because somehow when I refresh the user is being sent null for the WhiteLabelContext
What am I not seeing here?