mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2026-04-19 04:48:59 +01:00
MSAL browser migration changes
This commit is contained in:
@@ -278,7 +278,7 @@ export default class Explorer {
|
||||
updateUserContext({ aadToken: aadToken });
|
||||
useDataPlaneRbac.setState({ aadTokenUpdated: true });
|
||||
} catch (error) {
|
||||
if (error instanceof msal.AuthError && error.errorCode === msal.BrowserAuthErrorMessage.popUpWindowError.code) {
|
||||
if (error instanceof msal.AuthError && error.errorCode === msal.BrowserAuthErrorCodes.popupWindowError) {
|
||||
logConsoleError(
|
||||
"We were unable to establish authorization for this account, due to pop-ups being disabled in the browser.\nPlease enable pop-ups for this site and try again",
|
||||
);
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import { AuthError as msalAuthError, BrowserAuthErrorCodes as msalBrowserAuthErrorCodes } from "@azure/msal-browser";
|
||||
import {
|
||||
AuthError as msalAuthError,
|
||||
BrowserAuthErrorMessage as msalBrowserAuthErrorMessage,
|
||||
} from "@azure/msal-browser";
|
||||
import {
|
||||
Checkbox,
|
||||
ChoiceGroup,
|
||||
DefaultButton,
|
||||
Dropdown,
|
||||
IChoiceGroupOption,
|
||||
IDropdownOption,
|
||||
ISpinButtonStyles,
|
||||
IToggleStyles,
|
||||
Position,
|
||||
SpinButton,
|
||||
Stack,
|
||||
Toggle,
|
||||
Checkbox,
|
||||
ChoiceGroup,
|
||||
DefaultButton,
|
||||
Dropdown,
|
||||
IChoiceGroupOption,
|
||||
IDropdownOption,
|
||||
ISpinButtonStyles,
|
||||
IToggleStyles,
|
||||
Position,
|
||||
SpinButton,
|
||||
Stack,
|
||||
Toggle,
|
||||
} from "@fluentui/react";
|
||||
import { Accordion, AccordionHeader, AccordionItem, AccordionPanel, makeStyles } from "@fluentui/react-components";
|
||||
import { AuthType } from "AuthType";
|
||||
@@ -26,20 +23,20 @@ import { useDialog } from "Explorer/Controls/Dialog";
|
||||
import { useDatabases } from "Explorer/useDatabases";
|
||||
import { isFabric, isFabricNative } from "Platform/Fabric/FabricUtil";
|
||||
import {
|
||||
AppStateComponentNames,
|
||||
deleteAllStates,
|
||||
deleteState,
|
||||
hasState,
|
||||
loadState,
|
||||
saveState,
|
||||
AppStateComponentNames,
|
||||
deleteAllStates,
|
||||
deleteState,
|
||||
hasState,
|
||||
loadState,
|
||||
saveState,
|
||||
} from "Shared/AppStatePersistenceUtility";
|
||||
import {
|
||||
DefaultRUThreshold,
|
||||
LocalStorageUtility,
|
||||
StorageKey,
|
||||
getDefaultQueryResultsView,
|
||||
getRUThreshold,
|
||||
ruThresholdEnabled as isRUThresholdEnabled,
|
||||
DefaultRUThreshold,
|
||||
LocalStorageUtility,
|
||||
StorageKey,
|
||||
getDefaultQueryResultsView,
|
||||
getRUThreshold,
|
||||
ruThresholdEnabled as isRUThresholdEnabled,
|
||||
} from "Shared/StorageUtility";
|
||||
import * as StringUtility from "Shared/StringUtility";
|
||||
import { updateUserContext, userContext } from "UserContext";
|
||||
@@ -315,7 +312,7 @@ export const SettingsPane: FunctionComponent<{ explorer: Explorer }> = ({
|
||||
} catch (authError) {
|
||||
if (
|
||||
authError instanceof msalAuthError &&
|
||||
authError.errorCode === msalBrowserAuthErrorMessage.popUpWindowError.code
|
||||
authError.errorCode === msalBrowserAuthErrorCodes.popupWindowError
|
||||
) {
|
||||
logConsoleError(
|
||||
`We were unable to establish authorization for this account, due to pop-ups being disabled in the browser.\nPlease enable pop-ups for this site and click on "Login for Entra ID" button`,
|
||||
|
||||
@@ -49,6 +49,9 @@ export function decryptJWTToken(token: string) {
|
||||
}
|
||||
|
||||
export async function getMsalInstance() {
|
||||
// Compute the redirect bridge URL for MSAL v5 COOP handling
|
||||
const redirectBridgeUrl = `${window.location.origin}/redirectBridge.html`;
|
||||
|
||||
const msalConfig: msal.Configuration = {
|
||||
cache: {
|
||||
cacheLocation: "localStorage",
|
||||
@@ -56,14 +59,18 @@ export async function getMsalInstance() {
|
||||
auth: {
|
||||
authority: `${configContext.AAD_ENDPOINT}organizations`,
|
||||
clientId: "203f1145-856a-4232-83d4-a43568fba23d",
|
||||
// MSAL v5 requires redirect bridge for popup/silent flows (CG alert MVS-2026-vmmw-f85q)
|
||||
redirectUri: redirectBridgeUrl,
|
||||
},
|
||||
};
|
||||
|
||||
if (process.env.NODE_ENV === "development") {
|
||||
msalConfig.auth.redirectUri = "https://dataexplorer-dev.azurewebsites.net";
|
||||
if (process.env.NODE_ENV === "development" && !window.location.hostname.includes("localhost")) {
|
||||
msalConfig.auth.redirectUri = "https://dataexplorer-dev.azurewebsites.net/redirectBridge.html";
|
||||
}
|
||||
|
||||
const msalInstance = new msal.PublicClientApplication(msalConfig);
|
||||
// v3+ requires explicit initialization before using MSAL APIs
|
||||
await msalInstance.initialize();
|
||||
return msalInstance;
|
||||
}
|
||||
|
||||
|
||||
@@ -59,9 +59,12 @@ export function useAADAuth(config?: ConfigContext): ReturnType {
|
||||
return;
|
||||
}
|
||||
|
||||
// Use redirect bridge for MSAL v5 COOP handling (CG alert MVS-2026-vmmw-f85q)
|
||||
const redirectBridgeUrl = `${window.location.origin}/redirectBridge.html`;
|
||||
|
||||
try {
|
||||
const response = await msalInstance.loginPopup({
|
||||
redirectUri: config.msalRedirectURI,
|
||||
redirectUri: redirectBridgeUrl,
|
||||
scopes: [],
|
||||
});
|
||||
setLoggedIn();
|
||||
@@ -89,9 +92,11 @@ export function useAADAuth(config?: ConfigContext): ReturnType {
|
||||
if (!msalInstance || !config) {
|
||||
return;
|
||||
}
|
||||
// Use redirect bridge for MSAL v5 COOP handling (CG alert MVS-2026-vmmw-f85q)
|
||||
const redirectBridgeUrl = `${window.location.origin}/redirectBridge.html`;
|
||||
try {
|
||||
const response = await msalInstance.loginPopup({
|
||||
redirectUri: config.msalRedirectURI,
|
||||
redirectUri: redirectBridgeUrl,
|
||||
authority: `${config.AAD_ENDPOINT}${id}`,
|
||||
scopes: [],
|
||||
});
|
||||
@@ -120,7 +125,7 @@ export function useAADAuth(config?: ConfigContext): ReturnType {
|
||||
setArmToken(armToken);
|
||||
setAuthFailure(null);
|
||||
} catch (error) {
|
||||
if (error instanceof msal.AuthError && error.errorCode === msal.BrowserAuthErrorMessage.popUpWindowError.code) {
|
||||
if (error instanceof msal.AuthError && error.errorCode === msal.BrowserAuthErrorCodes.popupWindowError) {
|
||||
// This error can occur when acquireTokenWithMsal() has attempted to acquire token interactively
|
||||
// and user has popups disabled in browser. This fails as the popup is not the result of a explicit user
|
||||
// action. In this case, we display the failure and a link to repeat the operation. Clicking on the
|
||||
|
||||
14
src/redirectBridge.html
Normal file
14
src/redirectBridge.html
Normal file
@@ -0,0 +1,14 @@
|
||||
<!doctype html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Authentication Redirect</title>
|
||||
</head>
|
||||
<body>
|
||||
<!-- MSAL COOP Redirect Bridge - handles auth response from Identity Provider -->
|
||||
<div id="redirect-container">
|
||||
<p>Processing authentication...</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
16
src/redirectBridge.ts
Normal file
16
src/redirectBridge.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
/**
|
||||
* MSAL COOP Redirect Bridge
|
||||
*
|
||||
* This page handles the authentication response from the Identity Provider (IdP)
|
||||
* and broadcasts it to the main application frame. Required for msal-browser v5+
|
||||
* to securely handle auth responses when the IdP sets Cross-Origin-Opener-Policy headers.
|
||||
*
|
||||
* Security Note: This file must be bundled with your application, NOT loaded from a CDN.
|
||||
*
|
||||
* CG Alert: MVS-2026-vmmw-f85q
|
||||
*/
|
||||
import { broadcastResponseToMainFrame } from "@azure/msal-browser/redirect-bridge";
|
||||
|
||||
broadcastResponseToMainFrame().catch((error: unknown) => {
|
||||
console.error("MSAL redirect bridge error:", error);
|
||||
});
|
||||
11
src/types/msal-browser-redirect-bridge.d.ts
vendored
Normal file
11
src/types/msal-browser-redirect-bridge.d.ts
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
// Type declarations for @azure/msal-browser subpath exports
|
||||
// Required because tsconfig uses moduleResolution: "node" which doesn't support exports field
|
||||
|
||||
declare module "@azure/msal-browser/redirect-bridge" {
|
||||
/**
|
||||
* Processes the authentication response from the redirect URL.
|
||||
* For SSO and popup scenarios broadcasts it to the main frame.
|
||||
* For redirect scenario navigates to the home page.
|
||||
*/
|
||||
export function broadcastResponseToMainFrame(navigationClient?: unknown): Promise<void>;
|
||||
}
|
||||
Reference in New Issue
Block a user