Open Mohammed-Mounir opened 2 weeks ago
This behavior is happening because you're invoking loginPopup/Redirect on your redirectUri page, resulting in loops (for redirect) or popups attempting to open more popups. For popups the guidance is to use a blank page as your redirectUri, for redirects the guidance is to ensure handleRedirectPromise has completed before you invoke any other API.
Hi Thomas, This project has been using the loginPopup
method for about two years without any issues. although redirectUri
is set to /app
However, a few days ago, it was the first user to use this project on macOS, where he encountered a problem logging in with Edge and Chrome. They were only able to log in successfully using Safari.
After searching the internet, I found some users have reported issues with loginPopup
, so I attempted to switch to loginRedirect
. However, as I said before this caused the login page to refresh/redirect back same Login Page.
Anyway, I found the issue as being caused by a RequireAuth component used to wrap protected routes:
function RequireAuth({ children }) { const { userToken } = useAuth(); return userToken ? children : <Navigate to="/auth/login" replace /> }
For some reason, the RequireAuth component was causing this issue. Once I removed the RequireAuth component, the issue was resolved.
However, this led to another minor problem: after login, the app redirected to the main page /
, then to /auth/login
, and back to /
again. I suspected that the redirectUri
set to /app was causing this issue, but I don't have access to the Azure account to change it. As a workaround, I added a new route /app
in the React Router that opens the same Login Component as /auth/login
. Now, both routes /auth/login
and /app
direct to the same Login Component, which resolved the issue.
However, I'm not satisfied with this workaround. I'll reach out to the person who has access to the Azure account to request a change to the redirectUri
, and I'll follow up once it's resolved.
P.S. I'm using acquireTokenSilent to obtain an access token and authenticate with our server.
Edit: Here is the current full code after refactoring:
Router:
const Router = createBrowserRouter([
{
path: '/',
element: <MainLayout />,
children: [
{ path: 'auth/login', element: <Login /> },
{ path: 'app', element: <Login /> },
{
path: '',
element: <DashboardLayout />, // Previously with wrapped with RequireAuth
children: [
// Protected Routes
],
},
{ path: '*', element: <Navigate to="/" replace /> },
],
},
]);
export default Router;
useAuth
export default function useAuth() {
const { instance } = useMsal();
const [userToken, setUserToken] = useState<null | string>(null);
const [isLoading, setIsLoading] = useState(false);
const signIn = () => instance.loginRedirect(loginRequest);
const signOut = async () => {
setIsLoading(true);
try {
const refreshToken = sessionStorage.getItem('refresh_token');
sessionStorage.clear();
setUserToken(null);
if (refreshToken) {
await axiosPublic.post('/logout/', { refresh: refreshToken });
}
await instance.logoutRedirect();
} catch (error) {
const errorMessage = isAxiosError(error) ? error.response?.data : error;
showToastError(errorMessage);
} finally {
window.location.replace('/auth/login');
}
};
return { signIn, signOut, userToken, setUserToken, isLoading, setIsLoading };
}
AuthProvider
export const AuthProvider = ({ children }: { children: ReactNode }) => {
const { instance, accounts } = useMsal();
const account = useAccount(accounts[0] || {});
const auth = useAuth();
useEffect(() => {
if (account) {
auth.setIsLoading(true);
instance
.acquireTokenSilent({
...loginRequest,
account: account,
})
.then((auth: any) => {
const accessTokenObj = { 'MS-Access-Token': auth.accessToken };
return axiosPublic.post('/login/', accessTokenObj);
})
.then((res: any) => {
auth.setUserToken(res?.data.access);
sessionStorage.setItem('token', res?.data.access);
sessionStorage.setItem('refresh_token', res?.data.refresh);
sessionStorage.setItem(
'lastRefreshToken',
new Date().getTime().toString()
);
})
.catch((error: Error) => {
showToastError(isAxiosError(error) ? error.response?.data : error);
auth.signOut();
})
.finally(() => auth.setIsLoading(false));
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [account, instance]);
return <AuthContext.Provider value={auth}>{children}</AuthContext.Provider>;
};
export const useAuthContext = () => {
const context = useContext(AuthContext);
if (context === undefined)
console.error('AuthContext was used outside AuthProvider');
return context;
};
Login
function Login() {
const { userToken, signIn } = useAuthContext();
const navigate = useNavigate();
useEffect(() => {
if (userToken) navigate('/', { replace: true });
}, [navigate, userToken]);
const handleLogin = () => signIn();
return (
<main id={styles.main}>
...
</main>
);
}
export default Login;
This behavior is happening because you're invoking loginPopup/Redirect on your redirectUri page, resulting in loops (for redirect) or popups attempting to open more popups. For popups the guidance is to use a blank page as your redirectUri, for redirects the guidance is to ensure handleRedirectPromise has completed before you invoke any other API.
I checked the document regarding handleRedirectPromise, it says we don't need to call it and we should call acquireTokenSilent.
Core Library
MSAL.js (@azure/msal-browser)
Core Library Version
2.32.1
Wrapper Library
MSAL React (@azure/msal-react)
Wrapper Library Version
1.5.1
Public or Confidential Client?
Public
Description
Hello,
Currently, we are using loginPopup and acquireTokenPopup for authentication, on browsers like Edge and Chrome on Mac, and also on Android Chrome it's not working,
BrowserAuthError: block_nested_popups: Request was blocked inside a popup because MSAL detected it was running in a popup.
So I wanted to use loginRedirect instead of loginPopup, but it's not working at all, it's just refreshing/redirecting to the same/current Login page.
I followed the docs and searched all over the internet without any result...
P.S I have old versions of both msal-react and msal-browser, but I also tried latest version for both and getting the same result.
thanks
Error Message
BrowserAuthError: block_nested_popups: Request was blocked inside a popup because MSAL detected it was running in a popup.
MSAL Logs
No response
Network Trace (Preferrably Fiddler)
MSAL Configuration
Relevant Code Snippets
Reproduction Steps
Using loginPopup on Mac (Edge or Chrome) or Android (Chrome)
Expected Behavior
After login to Microsoft account, should be logged in.
Identity Provider
Entra ID (formerly Azure AD) / MSA
Browsers Affected (Select all that apply)
Chrome, Edge
Regression
No response
Source
External (Customer)