mirror of
https://github.com/Azure/cosmos-explorer.git
synced 2025-12-20 01:11:25 +00:00
Improve error handling when acquiring aad tokens (#1746)
* Mostly working - some cosmetic changes remaining. * Cosmetic changes and other tidy ups. * More clean up. * Move msal back to dependencies. Fix typo. * msal should be prod dependency * Revert msal package update as it is causing issues with unit test execution. * Add tracing for unhandled exceptions when acquiring tokens.
This commit is contained in:
@@ -2,9 +2,9 @@ import * as msal from "@azure/msal-browser";
|
||||
import { useBoolean } from "@fluentui/react-hooks";
|
||||
import * as React from "react";
|
||||
import { configContext } from "../ConfigContext";
|
||||
import { getMsalInstance } from "../Utils/AuthorizationUtils";
|
||||
import { acquireTokenWithMsal, getMsalInstance } from "../Utils/AuthorizationUtils";
|
||||
|
||||
const msalInstance = getMsalInstance();
|
||||
const msalInstance = await getMsalInstance();
|
||||
|
||||
const cachedAccount = msalInstance.getAllAccounts()?.[0];
|
||||
const cachedTenantId = localStorage.getItem("cachedTenantId");
|
||||
@@ -18,6 +18,13 @@ interface ReturnType {
|
||||
tenantId: string;
|
||||
account: msal.AccountInfo;
|
||||
switchTenant: (tenantId: string) => void;
|
||||
authFailure: AadAuthFailure;
|
||||
}
|
||||
|
||||
export interface AadAuthFailure {
|
||||
failureMessage: string;
|
||||
failureLinkTitle?: string;
|
||||
failureLinkAction?: () => void;
|
||||
}
|
||||
|
||||
export function useAADAuth(): ReturnType {
|
||||
@@ -28,6 +35,7 @@ export function useAADAuth(): ReturnType {
|
||||
const [tenantId, setTenantId] = React.useState<string>(cachedTenantId);
|
||||
const [graphToken, setGraphToken] = React.useState<string>();
|
||||
const [armToken, setArmToken] = React.useState<string>();
|
||||
const [authFailure, setAuthFailure] = React.useState<AadAuthFailure>(undefined);
|
||||
|
||||
msalInstance.setActiveAccount(account);
|
||||
const login = React.useCallback(async () => {
|
||||
@@ -61,24 +69,60 @@ export function useAADAuth(): ReturnType {
|
||||
[account, tenantId],
|
||||
);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (account && tenantId) {
|
||||
Promise.all([
|
||||
msalInstance.acquireTokenSilent({
|
||||
authority: `${configContext.AAD_ENDPOINT}${tenantId}`,
|
||||
scopes: [`${configContext.GRAPH_ENDPOINT}/.default`],
|
||||
}),
|
||||
msalInstance.acquireTokenSilent({
|
||||
authority: `${configContext.AAD_ENDPOINT}${tenantId}`,
|
||||
scopes: [`${configContext.ARM_ENDPOINT}/.default`],
|
||||
}),
|
||||
]).then(([graphTokenResponse, armTokenResponse]) => {
|
||||
setGraphToken(graphTokenResponse.accessToken);
|
||||
setArmToken(armTokenResponse.accessToken);
|
||||
const acquireTokens = React.useCallback(async () => {
|
||||
if (!(account && tenantId)) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const armToken = await acquireTokenWithMsal(msalInstance, {
|
||||
authority: `${configContext.AAD_ENDPOINT}${tenantId}`,
|
||||
scopes: [`${configContext.ARM_ENDPOINT}/.default`],
|
||||
});
|
||||
|
||||
setArmToken(armToken);
|
||||
setAuthFailure(null);
|
||||
} catch (error) {
|
||||
if (error instanceof msal.AuthError && error.errorCode === msal.BrowserAuthErrorMessage.popUpWindowError.code) {
|
||||
// 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
|
||||
// link is a user action so it will work even if popups have been disabled.
|
||||
// See: https://github.com/AzureAD/microsoft-authentication-library-for-js/issues/76#issuecomment-324787539
|
||||
setAuthFailure({
|
||||
failureMessage:
|
||||
"We were unable to establish authorization for this account, due to pop-ups being disabled in the browser.\nPlease click below to retry authorization without requiring popups being enabled.",
|
||||
failureLinkTitle: "Retry Authorization",
|
||||
failureLinkAction: acquireTokens,
|
||||
});
|
||||
} else {
|
||||
const errorJson = JSON.stringify(error);
|
||||
setAuthFailure({
|
||||
failureMessage: `We were unable to establish authorization for this account, due to the following error: \n${errorJson}`,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const graphToken = await acquireTokenWithMsal(msalInstance, {
|
||||
authority: `${configContext.AAD_ENDPOINT}${tenantId}`,
|
||||
scopes: [`${configContext.GRAPH_ENDPOINT}/.default`],
|
||||
});
|
||||
|
||||
setGraphToken(graphToken);
|
||||
} catch (error) {
|
||||
// Graph token is used only for retrieving user photo at the moment, so
|
||||
// it's not critical if this fails.
|
||||
console.warn("Error acquiring graph token: " + error);
|
||||
}
|
||||
}, [account, tenantId]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (account && tenantId && !authFailure) {
|
||||
acquireTokens();
|
||||
}
|
||||
}, [account, tenantId, acquireTokens, authFailure]);
|
||||
|
||||
return {
|
||||
account,
|
||||
tenantId,
|
||||
@@ -88,5 +132,6 @@ export function useAADAuth(): ReturnType {
|
||||
login,
|
||||
logout,
|
||||
switchTenant,
|
||||
authFailure,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useCommandBar } from "Explorer/Menus/CommandBar/CommandBarComponentAdap
|
||||
import { useSelectedNode } from "Explorer/useSelectedNode";
|
||||
import { scheduleRefreshDatabaseResourceToken } from "Platform/Fabric/FabricUtil";
|
||||
import { getNetworkSettingsWarningMessage } from "Utils/NetworkUtility";
|
||||
import { logConsoleError } from "Utils/NotificationConsoleUtils";
|
||||
import { useQueryCopilot } from "hooks/useQueryCopilot";
|
||||
import { ReactTabKind, useTabs } from "hooks/useTabs";
|
||||
import { useEffect, useState } from "react";
|
||||
@@ -35,7 +36,7 @@ import {
|
||||
import { extractFeatures } from "../Platform/Hosted/extractFeatures";
|
||||
import { DefaultExperienceUtility } from "../Shared/DefaultExperienceUtility";
|
||||
import { Node, PortalEnv, updateUserContext, userContext } from "../UserContext";
|
||||
import { getAuthorizationHeader, getMsalInstance } from "../Utils/AuthorizationUtils";
|
||||
import { acquireTokenWithMsal, getAuthorizationHeader, getMsalInstance } from "../Utils/AuthorizationUtils";
|
||||
import { isInvalidParentFrameOrigin, shouldProcessMessage } from "../Utils/MessageValidation";
|
||||
import { listKeys } from "../Utils/arm/generatedClients/cosmos/databaseAccounts";
|
||||
import { DatabaseAccountListKeysResult } from "../Utils/arm/generatedClients/cosmos/types";
|
||||
@@ -243,16 +244,19 @@ async function configureHostedWithAAD(config: AAD): Promise<Explorer> {
|
||||
let keys: DatabaseAccountListKeysResult = {};
|
||||
if (account.properties?.documentEndpoint) {
|
||||
const hrefEndpoint = new URL(account.properties.documentEndpoint).href.replace(/\/$/, "/.default");
|
||||
const msalInstance = getMsalInstance();
|
||||
const msalInstance = await getMsalInstance();
|
||||
const cachedAccount = msalInstance.getAllAccounts()?.[0];
|
||||
msalInstance.setActiveAccount(cachedAccount);
|
||||
const cachedTenantId = localStorage.getItem("cachedTenantId");
|
||||
const aadTokenResponse = await msalInstance.acquireTokenSilent({
|
||||
forceRefresh: true,
|
||||
scopes: [hrefEndpoint],
|
||||
authority: `${configContext.AAD_ENDPOINT}${cachedTenantId}`,
|
||||
});
|
||||
aadToken = aadTokenResponse.accessToken;
|
||||
try {
|
||||
aadToken = await acquireTokenWithMsal(msalInstance, {
|
||||
forceRefresh: true,
|
||||
scopes: [hrefEndpoint],
|
||||
authority: `${configContext.AAD_ENDPOINT}${cachedTenantId}`,
|
||||
});
|
||||
} catch (authError) {
|
||||
logConsoleError("Failed to acquire authorization token: " + authError);
|
||||
}
|
||||
}
|
||||
try {
|
||||
if (!account.properties.disableLocalAuth) {
|
||||
|
||||
Reference in New Issue
Block a user