
如何用 MSAL 在 Refine 中接入 Azure AD B2C 登錄【免費下載鏈接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.項目地址: https://gitcode.com/GitHub_Trending/re/refine假設你已經在用 Refine 做了一個內部管理應用現在不想再維護自己的賬號密碼而是讓企業用戶直接用 Azure AD B2C 的社交賬號、企業賬號或本地賬號登錄。目標結果是應用打開時先經過一個登錄頁點擊按鈕跳轉到 Azure B2C 登錄頁登錄成功后應用保存訪問令牌并自動把它附加到后續的數據請求上。Refine 官方的 Azure AD 文檔 走的就是這條路用 Microsoft Authentication LibraryMSAL的 JavaScript 版本azure/msal-browser加 React 輔助包azure/msal-react完成認證再由 Refine 的authProvider接管登錄態判斷。文檔以 Azure AD B2C 為例并說明用普通 Azure AD 做認證的步驟與之非常相似。下面按文檔給出的順序逐步接入。代碼示例基于 antd UI 集成refinedev/antd和refinedev/react-router路由。安裝 MSAL 依賴在 Refine 項目根目錄安裝兩個包三個包管理器任選其一# npm npm i azure/msal-browser azure/msal-react # pnpm pnpm add azure/msal-browser azure/msal-react # yarn yarn add azure/msal-browser azure/msal-react這兩個包分別提供 MSAL 的瀏覽器端實現PublicClientApplication、事件回調等和 React 集成MsalProvider、useMsal、useIsAuthenticated等 hook。創建 MSAL 配置文件在src/config.ts中創建 MSAL 配置。文檔給出的完整內容如下其中帶YOUR_前綴的字符串是占位符需要替換為你自己在 Azure 租戶中申請到的值每個占位符對應的環境變量寫法文檔已用注釋保留在代碼里按注釋中的寫法接入自己的環境變量即可import { Configuration, LogLevel } from azure/msal-browser; export const msalConfig: Configuration { auth: { clientId: YOUR_CLIENT_ID, //${process.env.REACT_APP_AZURE_AAD_CLIENT_ID}, authority: YOUR_AUTHORITY, //https://${process.env.REACT_APP_AZURE_AAD_TENANT_NAME}.b2clogin.com/${process.env.REACT_APP_AZURE_AAD_TENANT_NAME}.onmicrosoft.com/${process.env.REACT_APP_AZURE_AAD_POLICY_NAME}, knownAuthorities: [YOUR_KNOWN_AUTHORITIES], //[${process.env.REACT_APP_AZURE_AAD_TENANT_NAME}.b2clogin.com], redirectUri: http://localhost:3000/, // Replace appropriately postLogoutRedirectUri: window.location.origin, }, cache: { cacheLocation: sessionStorage, // This configures where your cache will be stored storeAuthStateInCookie: false, // Set this to true if you are having issues on IE11 or Edge }, }; // Add scopes here for ID token to be used at Microsoft identity platform endpoints. export const loginRequest { scopes: [User.Read] }; export const tokenRequest { scopes: [...] // Replace ... with your custom scopes }; // Add the endpoints here for Microsoft Graph API services youd like to use. export const graphConfig { graphMeEndpoint: ENTER_THE_GRAPH_ENDPOINT_HERE/v1.0/me };幾個需要替換或確認的位置clientId、authority、knownAuthorities來自你的 Azure B2C 租戶與客戶端應用注冊。按代碼注釋中的環境變量形式拼接后authority形如https://tenant.b2clogin.com/tenant.onmicrosoft.com/policyknownAuthorities為tenant.b2clogin.com。redirectUri登錄完成后跳回的地址文檔示例為http://localhost:3000/注釋要求按你的實際部署地址替換Replace appropriately。tokenRequest.scopes代碼里的...是占位文檔注釋明確要求替換成你需要的自定義 scopes。graphConfig只有你要調用 Microsoft Graph API 時才需要按注釋填入實際的 Graph 端點即可不需要 Graph 可以忽略這一節。文檔建議用環境變量管理這些配置參數而不是把值直接寫死在代碼里。用 MsalProvider 包裹根組件修改src/index.tsx創建PublicClientApplication實例監聽LOGIN_SUCCESS事件——登錄成功后先靜默獲取訪問令牌成功后寫入localStorage靜默獲取失敗時回退到彈窗方式獲取。同時用MsalProvider包裹整個應用import React from react; import ReactDOM from react-dom/client; import { EventType, PublicClientApplication, AccountInfo, EventPayload, SilentRequest, } from azure/msal-browser; import { MsalProvider } from azure/msal-react; import App, { TOKEN_KEY } from ./App; import { msalConfig, tokenRequest } from ./config; const msalInstance new PublicClientApplication(msalConfig); msalInstance.addEventCallback(async (event) { if (event.eventType EventType.LOGIN_SUCCESS) { const payload: EventPayload event.payload; msalInstance.setActiveAccount(payload as AccountInfo); let account msalInstance.getActiveAccount(); const request: SilentRequest { ...tokenRequest, account: account!, }; try { // Silently acquires an access token which is then attached to a request for API access const response await msalInstance.acquireTokenSilent(request); console.log(Fetching access token: success); console.log(Scopes, response.scopes); console.log(Token Type, response.tokenType); localStorage.setItem(TOKEN_KEY, response.accessToken); } catch (e) { msalInstance.acquireTokenPopup(request).then((response) { localStorage.setItem(TOKEN_KEY, response.accessToken); }); } } }); const root ReactDOM.createRoot( document.getElementById(root) as HTMLElement, ); root.render( React.StrictMode MsalProvider instance{msalInstance} App / /MsalProvider /React.StrictMode, );這里TOKEN_KEY是從App.tsx導入的常量取值refine-auth后面保存令牌、讀取令牌都通過它進行。覆蓋 Refine 的登錄頁Refine 允許覆蓋默認登錄頁。在src/下創建login.tsx用一個按鈕觸發useLogin()實際跳轉由authProvider.login里的loginRedirect()完成import React from react; import { useLogin } from refinedev/core; import { Layout, Button } from antd; const LoginPage () { const SignInButton () { const { mutate: login } useLogin(); return ( Button typeprimary sizelarge block onClick{() login()} Sign in /Button ); }; return ( Layout style{{ background: radial-gradient(50% 50% at 50% 50%, #63386A 0%, #310438 100%), backgroundSize: cover, }} div style{{ height: 100vh, display: flex }} div style{{ maxWidth: 200px, margin: auto }} div style{{ marginBottom: 28px }} img src./refine.svg altRefine / /div SignInButton / /div /div /Layout ); }; export default LoginPage;編寫 authProvider 與請求令牌注入核心在src/App.tsx定義AuthProvider讓 Refine 的登錄態完全由 MSAL 的賬號和令牌驅動同時給 axios 實例加請求攔截器把localStorage中的令牌寫進Authorization請求頭import { Refine, AuthProvider, Authenticated } from refinedev/core; import { Layout, ErrorComponent } from refinedev/antd; import routerProvider, { NavigateToResource, CatchAllNavigate, } from refinedev/react-router; import dataProvider from refinedev/simple-rest; import { useIsAuthenticated, useMsal } from azure/msal-react; import { AccountInfo, SilentRequest } from azure/msal-browser; import axios from axios; import { BrowserRouter, Routes, Route, Outlet } from react-router; import LoginPage from ./login; import { tokenRequest } from ./config; export const TOKEN_KEY refine-auth; export const axiosInstance axios.create(); axiosInstance.interceptors.request.use( // Here we can perform any function wed like on the request (config) { // Retrieve the token from local storage const token localStorage.getItem(TOKEN_KEY); // Check if the header property exists if (config.headers) { // Set the Authorization header if it exists config.headers[Authorization] Bearer ${token}; } return config; }, ); const App: React.FC () { const API_URL https://api.fake-rest.refine.dev; const isAuthenticated useIsAuthenticated(); const { instance, inProgress, accounts } useMsal(); if (inProgress login || inProgress handleRedirect) { return divLoading.../div; } const account: AccountInfo accounts[0]; const request: SilentRequest { ...tokenRequest, account, }; const authProvider: AuthProvider { login: async () { instance.loginRedirect(); // Pick the strategy you prefer i.e. redirect or popup return { success: true, }; }, register: async () ({ success: true, }), resetPassword: async () ({ success: true, }), updatePassword: async () ({ success: true, }), logout: async () ({ success: true, }), check: async () { try { if (account) { const token await instance.acquireTokenSilent(request); localStorage.setItem(TOKEN_KEY, token.accessToken); return { authenticated: true, }; } else { return { authenticated: false, redirectTo: /login, }; } } catch (e) { return { authenticated: false, redirectTo: /login, }; } }, onError: async (error) { console.error(error); return { error }; }, getPermissions: async () null, getIdentity: async (): PromiseAccountInfo { if (account null || account undefined) { return null; } return account; }, }; return ( BrowserRouter Refine routerProvider{routerProvider} dataProvider{dataProvider(API_URL, axiosInstance)} authProvider{authProvider} resources{[ { name: posts, list: /posts, }, ]} Routes Route element{ Authenticated fallback{CatchAllNavigate to/login /} Layout Outlet / /Layout /Authenticated } Route path/posts element{divdummy list page/div} / /Route Route element{ Authenticated fallback{Outlet /} NavigateToResource / /Authenticated } Route path/login element{LoginPage /} / /Route Route path* element{ErrorComponent /} / /Routes /Refine /BrowserRouter ); }; export default App;各部分的作用login調用instance.loginRedirect()把瀏覽器跳轉到 Azure B2C 登錄頁。注釋說明也可以換成你偏好的策略redirect 或 popup。check這是登錄態判斷的核心。存在賬號時靜默獲取令牌并寫入localStorage返回authenticated: true沒有賬號或獲取失敗時返回authenticated: false并攜帶redirectTo: /login由Authenticated組件負責跳轉。getIdentity直接返回當前AccountInfo供 UI 展示當前用戶。路由結構受保護頁面套在Authenticated里未登錄時回退到/login/login頁本身又包了一層Authenticated已登錄用戶訪問它時會被NavigateToResource帶回業務頁。數據請求走dataProvider(API_URL, axiosInstance)文檔示例中API_URL是https://api.fake-rest.refine.dev接入自己的后端時替換為你的 API 地址即可axiosInstance的攔截器保證每個請求自動帶上Bearer令牌。register、resetPassword、updatePassword、logout在示例中統一返回success: true因為賬號生命周期由 Azure B2C 側管理應用內不做這些操作。驗證接入是否生效文檔代碼中體現的驗證點如下按流程核對應用啟動時若 MSAL 正在執行login或handleRedirect頁面顯示Loading...說明重定向流程在走。未登錄狀態訪問業務頁面check返回未認證頁面落到/login并出現 Sign in 按鈕。點擊 Sign in 后被重定向到 Azure B2C 登錄頁登錄成功后LOGIN_SUCCESS回調觸發靜默獲取令牌。文檔示例在成功后打印的日志文檔示例輸出Fetching access token: success Scopes ... Token Type ...令牌以refine-auth為鍵存入localStorage后續經axiosInstance發出的請求Authorization頭為Bearer token。已登錄狀態刷新頁面check能靜默拿到令牌并返回authenticated: true應用直接進入業務頁而不是/login。限制與注意點示例的登錄頁和 App 基于 antdrefinedev/antd的Layout、ErrorComponent與refinedev/react-router使用其他 UI 包或路由集成時對應導入需要按各自的包調整MSAL 相關邏輯不變。文檔以 Azure AD B2C 為例并說明認證普通 Azure AD 的步驟與之非常相似。redirectUri必須與瀏覽器中實際運行地址一致示例為本地開發地址http://localhost:3000/部署到別的環境時按代碼注釋替換。完整可核對的原始步驟見 documentation/docs/advanced-tutorials/auth/azure-ad.mdMSAL 各配置項的更多說明見 MSAL 官方文檔。【免費下載鏈接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.項目地址: https://gitcode.com/GitHub_Trending/re/refine創作聲明:本文部分內容由AI輔助生成(AIGC),僅供參考