React+MSAL+Azure SSO登录跳转异常,请求技术支持
React + MSAL + Azure SSO 跳转异常问题排查与解决
问题现象
点击SSO登录按钮后,短暂跳转到/home页面随即返回/signin,网络面板无token生成。Azure端已完成以下配置:
- redirectUri设置为
http://localhost:5173/home - 添加openid、email、profile权限范围
- 启用access token和id token
相关代码(修正后)
Authconfig.js
export const msalConfig = { auth: { clientId: "client-id", authority: "https://login.microsoftonline.com/tenant-id", redirectUri: "http://localhost:5173/home", }, cache: { cacheLocation: "sessionStorage", storeAuthStateInCookie: false, // 修复原代码拼写错误:issuesstoreAuthStateInCookie }, }; // 补充缺失的loginRequest配置 export const loginRequest = { scopes: ["openid", "email", "profile"] };
main.jsx
import React from 'react'; import ReactDOM from 'react-dom/client'; import { MsalProvider } from '@azure/msal-react'; import { PublicClientApplication } from '@azure/msal-browser'; import { msalConfig } from './Authconfig'; import { RouterProvider, createBrowserRouter } from 'react-router-dom'; import { ProtectedRoutes } from './utils/ProtectedRoutes.jsx'; import { AboutComponent } from './AboutComponent.jsx'; import { AdminComponent } from './AdminComponent.jsx'; import { BuildComponent } from './BuildComponent.jsx'; import { ContactusComponent } from './ContactusComponent.jsx'; import { FeedbackComponent } from './FeedbackComponent.jsx'; import { LandingPageComponent } from './LandingPageComponent.jsx'; import { LoginPageComponent } from './LoginPageComponent.jsx'; import { ReleasenotesComponent } from './ReleasenotesComponent.jsx'; import { UserHomeComponent } from './UserHomeComponent.jsx'; import { PricingComponent } from './PricingComponent.jsx'; import { UserProfileComponent } from './UserProfileComponent.jsx'; import './index.css'; import { AuthProvider } from './utils/AuthContext'; const msalInstance = new PublicClientApplication(msalConfig); const router = createBrowserRouter([ { path: '/', element: <LandingPageComponent />, errorElement: <div>404 not found</div>, children: [ { path: '', element: <AboutComponent /> }, { path: 'pricing', element: <PricingComponent /> }, { path: 'releasenotes', element: <ReleasenotesComponent /> }, { path: 'contactus', element: <ContactusComponent /> }, { path: 'signin', element: <LoginPageComponent /> }, { element: <ProtectedRoutes />, children: [ { path: 'home', element: <UserHomeComponent /> }, { path: 'build', element: <BuildComponent /> }, { path: 'admin', element: <AdminComponent /> }, { path: 'userprofile', element: <UserProfileComponent /> }, { path: 'feedback', element: <FeedbackComponent /> }, ], }, ], }, ]); ReactDOM.createRoot(document.getElementById('root')).render( <React.StrictMode> <MsalProvider instance={msalInstance}> <AuthProvider> <RouterProvider router={router} /> </AuthProvider> </MsalProvider> </React.StrictMode> );
LoginPageComponent.jsx
import React, { useContext, useEffect } from 'react'; import { useMsal } from '@azure/msal-react'; import { loginRequest } from './Authconfig'; import { useNavigate } from 'react-router-dom'; import loginimage from './assets/loginimage.svg'; import { AuthContext } from './utils/AuthContext'; export const LoginPageComponent = () => { const { instance } = useMsal(); const navigate = useNavigate(); const { login } = useContext(AuthContext); const handleLogin = () => { instance.loginRedirect(loginRequest); }; useEffect(() => { instance.handleRedirectPromise() .then((response) => { if (response) { console.log('Authentication Response:', response); login(response.account); // 移除手动导航:redirectUri已指向/home,无需重复跳转 } }) .catch((error) => { console.error('Authentication Error:', error); }); }, [instance, navigate, login]); return ( <div className="bg-white"> <div className="grid grid-cols-2 justify-center items-center m-20"> <div> <img loading="lazy" src={loginimage} className="self-end max-w-full aspect-square w-[540px]" /> </div> <div className="flex flex-col ml-5"> <div className="flex flex-col p-10 mt-5 w-fit items-center justify-center rounded-lg shadow-sm bg-sky-600 bg-opacity-20 text-zinc-800 max-md:px-5 max-md:mt-10"> <div className="text-4xl font-black text-sky-600"> <span className="font-medium">Welcome to</span> <br /> <span className="text-5xl text-sky-600 leading-[62px]"> buildNow Portal </span> </div> <button onClick={handleLogin} className="w-96 px-8 py-3 ml-6 rounded-lg bg-indigo-500 text-gray-100 border border-gray-200 placeholder-gray-500 text-sm tracking-wide font-semibold hover:bg-indigo-700 transition-all duration-300 ease-in-out flex items-center justify-center focus:shadow-outline focus:outline-none mt-5"> <svg className="w-6 h-6 -ml-2" fill="none" stroke="currentColor"> <path d="M16 21v-2a4 4 0 00-4-4H5a4 4 0 00-4 4v2" /> <circle cx="8.5" cy="7" r="4" /> <path d="M20 8v6M23 11h-6" /> </svg> <span className="ml-3"> LOGIN </span> </button> </div> </div> </div> </div> ); };
AuthContext.jsx
import React, { createContext, useState, useEffect } from 'react'; import { useMsal } from '@azure/msal-react'; const AuthContext = createContext(); const AuthProvider = ({ children }) => { const { instance } = useMsal(); const [user, setUser] = useState(() => { const storedUser = localStorage.getItem('user'); return storedUser ? JSON.parse(storedUser) : null; }); // 同步MSAL账户状态到AuthContext useEffect(() => { const accounts = instance.getAllAccounts(); if (accounts.length > 0 && !user) { setUser(accounts[0]); localStorage.setItem('user', JSON.stringify(accounts[0])); } }, [instance, user]); const login = (account) => { setUser(account); localStorage.setItem('user', JSON.stringify(account)); }; const logout = () => { instance.logoutRedirect(); setUser(null); localStorage.removeItem('user'); }; return ( <AuthContext.Provider value={{ user, setUser, login, logout }}> {children} </AuthContext.Provider> ); }; export { AuthContext, AuthProvider };
核心解决步骤
- 修复缓存配置拼写错误:原代码中
issuesstoreAuthStateInCookie是拼写错误,改为storeAuthStateInCookie,否则MSAL无法正确管理认证缓存。 - 补充loginRequest配置:必须明确请求的权限范围,否则MSAL不会生成token。
- 移除重复导航逻辑:redirectUri已设置为
/home,Azure会自动跳转,手动调用navigate会导致路由状态冲突。 - 同步MSAL与AuthContext状态:添加监听MSAL账户变化的逻辑,确保ProtectedRoutes能正确识别登录状态。
- 检查ProtectedRoutes实现:确保路由守卫正确读取AuthContext中的user状态,示例实现:
import React, { useContext } from 'react'; import { Navigate, Outlet } from 'react-router-dom'; import { AuthContext } from './AuthContext'; export const ProtectedRoutes = () => { const { user } = useContext(AuthContext); return user ? <Outlet /> : <Navigate to="/signin" />; };
- 验证Azure配置:确认redirectUri完全匹配(协议、端口、路径无偏差),权限已授予管理员同意(租户应用),且SPA应用使用授权码流程+PKCE。
内容的提问来源于stack exchange,提问作者Airin Anil
相关产品推荐
相关产品推荐

