stuck in a redirect loop, need to work that out

This commit is contained in:
Ashley Stanton-Nurse 2024-06-07 11:03:20 -07:00
parent 06d4829422
commit 4866d3c902
No known key found for this signature in database
7 changed files with 102 additions and 19 deletions

View File

@ -238,6 +238,15 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
updateConfigContext({ platform }); updateConfigContext({ platform });
} }
} }
if (window.location.origin !== configContext.hostedExplorerURL) {
if (window.location.origin === "https://localhost:1234") {
// Special case for localhost, we need to send them to 'hostedExplorer.html'.
updateConfigContext({ hostedExplorerURL: "https://localhost:1234/hostedExplorer.html" });
} else {
const newOrigin = window.location.origin.endsWith("/") ? window.location.origin : `${window.location.origin}/`;
updateConfigContext({ hostedExplorerURL: newOrigin });
}
}
} catch (error) { } catch (error) {
console.error("No configuration file found using defaults"); console.error("No configuration file found using defaults");
} }
@ -245,3 +254,4 @@ export async function initializeConfiguration(): Promise<ConfigContext> {
} }
export { configContext }; export { configContext };

View File

@ -1,20 +1,40 @@
import { PrimaryButton, Stack, Text } from "@fluentui/react"; import { PrimaryButton, Stack, Text } from "@fluentui/react";
import { AuthType } from "AuthType";
import { configContext } from "ConfigContext";
import { userContext } from "UserContext";
import * as React from "react"; import * as React from "react";
export const OpenFullScreen: React.FunctionComponent = () => { export const OpenFullScreen: React.FunctionComponent = () => {
const searchParams = new URLSearchParams();
let hasAccountContext = false;
let requiresConnectionString = false;
if (userContext.authType === AuthType.AAD) {
if (userContext.subscriptionId && userContext.databaseAccount) {
searchParams.append("subscription", userContext.subscriptionId);
searchParams.append("account", userContext.databaseAccount.id);
searchParams.append("authType", "entra");
hasAccountContext = true;
}
} else if (userContext.authType === AuthType.MasterKey || userContext.authType === AuthType.ResourceToken) {
searchParams.append("authType", "connectionstring")
requiresConnectionString = true;
}
return ( return (
<> <>
<div style={{ padding: "34px" }}> <div style={{ padding: "34px" }}>
<Stack tokens={{ childrenGap: 10 }}> <Stack tokens={{ childrenGap: 10 }}>
<Text> <Text>
Open this database account in a new browser tab with Cosmos DB Explorer. You can connect using your Open this database account in a new browser tab with Cosmos DB Explorer.
Microsoft account or a connection string. {requiresConnectionString && " You'll need to provide a connection string."}
{hasAccountContext && " You may be prompted to sign in with Entra ID, and then you'll be redirected back to this account."}
Open tabs and queries will not be carried over, but will remain in this tab.
</Text> </Text>
<Stack horizontal tokens={{ childrenGap: 10 }}> <Stack horizontal tokens={{ childrenGap: 10 }}>
<PrimaryButton <PrimaryButton
onClick={() => { href={`${configContext.hostedExplorerURL}?${searchParams.toString()}`}
window.open("https://cosmos.azure.com/", "_blank"); target="_blank"
}}
text="Open" text="Open"
iconProps={{ iconName: "OpenInNewWindow" }} iconProps={{ iconName: "OpenInNewWindow" }}
/> />

View File

@ -1,6 +1,7 @@
import { initializeIcons } from "@fluentui/react"; import { initializeIcons } from "@fluentui/react";
import { useBoolean } from "@fluentui/react-hooks"; import { useBoolean } from "@fluentui/react-hooks";
import { AadAuthorizationFailure } from "Platform/Hosted/Components/AadAuthorizationFailure"; import { AadAuthorizationFailure } from "Platform/Hosted/Components/AadAuthorizationFailure";
import { getMsalInstance } from "Utils/AuthorizationUtils";
import * as React from "react"; import * as React from "react";
import { render } from "react-dom"; import { render } from "react-dom";
import ChevronRight from "../images/chevron-right.svg"; import ChevronRight from "../images/chevron-right.svg";
@ -42,6 +43,23 @@ const App: React.FunctionComponent = () => {
const ref = React.useRef<HTMLIFrameElement>(); const ref = React.useRef<HTMLIFrameElement>();
React.useEffect(() => { React.useEffect(() => {
(async () => {
const params = new URLSearchParams(window.location.search);
if (params.get("authType") === "entra") {
const msalInstance = await getMsalInstance();
const response = await msalInstance.handleRedirectPromise();
if (response) {
console.log("Redirect Promise Response", response);
}
else {
// Send the user to log in immediately
console.log("Starting non-interactive login");
login(false);
}
return;
}
})();
// If ref.current is undefined no iframe has been rendered // If ref.current is undefined no iframe has been rendered
if (ref.current) { if (ref.current) {
// In hosted mode, we can set global properties directly on the child iframe. // In hosted mode, we can set global properties directly on the child iframe.

View File

@ -1,4 +1,5 @@
import React, { useState } from "react"; import { getMsalInstance } from "Utils/AuthorizationUtils";
import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom"; import ReactDOM from "react-dom";
import Arrow from "../images/Arrow.svg"; import Arrow from "../images/Arrow.svg";
import CosmosDB_20170829 from "../images/CosmosDB_20170829.svg"; import CosmosDB_20170829 from "../images/CosmosDB_20170829.svg";
@ -9,11 +10,18 @@ import "../less/index.less";
const Index = (): JSX.Element => { const Index = (): JSX.Element => {
const [navigationSelection, setNavigationSelection] = useState("quickstart"); const [navigationSelection, setNavigationSelection] = useState("quickstart");
const quickstart_click = () => { const quickstart_click = () => {
setNavigationSelection("quickstart"); setNavigationSelection("quickstart");
}; };
useEffect(() => {
(async () => {
const msalInstance = await getMsalInstance();
console.log("handleRedirectPromise");
await msalInstance.handleRedirectPromise();
})();
}, []);
const explorer_click = () => { const explorer_click = () => {
setNavigationSelection("explorer"); setNavigationSelection("explorer");
}; };

View File

@ -13,12 +13,12 @@ import { CollectionCreation, CollectionCreationDefaults } from "./Shared/Constan
interface ThroughputDefaults { interface ThroughputDefaults {
fixed: number; fixed: number;
unlimited: unlimited:
| number | number
| { | {
collectionThreshold: number; collectionThreshold: number;
lessThanOrEqualToThreshold: number; lessThanOrEqualToThreshold: number;
greatThanThreshold: number; greatThanThreshold: number;
}; };
unlimitedmax: number; unlimitedmax: number;
unlimitedmin: number; unlimitedmin: number;
shared: number; shared: number;
@ -192,3 +192,4 @@ function apiType(account: DatabaseAccount | undefined): ApiType {
} }
export { updateUserContext, userContext }; export { updateUserContext, userContext };

View File

@ -128,7 +128,7 @@ export const allowedGraphEndpoints: ReadonlyArray<string> = ["https://graph.micr
export const allowedArcadiaEndpoints: ReadonlyArray<string> = ["https://workspaceartifacts.projectarcadia.net"]; export const allowedArcadiaEndpoints: ReadonlyArray<string> = ["https://workspaceartifacts.projectarcadia.net"];
export const allowedHostedExplorerEndpoints: ReadonlyArray<string> = ["https://cosmos.azure.com/"]; export const allowedHostedExplorerEndpoints: ReadonlyArray<string> = ["https://cosmos.azure.com/", "https://localhost:1234/"];
export const allowedMsalRedirectEndpoints: ReadonlyArray<string> = [ export const allowedMsalRedirectEndpoints: ReadonlyArray<string> = [
"https://cosmos-explorer-preview.azurewebsites.net/", "https://cosmos-explorer-preview.azurewebsites.net/",

View File

@ -13,7 +13,7 @@ interface ReturnType {
isLoggedIn: boolean; isLoggedIn: boolean;
graphToken: string; graphToken: string;
armToken: string; armToken: string;
login: () => void; login: (userTriggered?: boolean) => void;
logout: () => void; logout: () => void;
tenantId: string; tenantId: string;
account: msal.AccountInfo; account: msal.AccountInfo;
@ -37,8 +37,21 @@ export function useAADAuth(): ReturnType {
const [armToken, setArmToken] = React.useState<string>(); const [armToken, setArmToken] = React.useState<string>();
const [authFailure, setAuthFailure] = React.useState<AadAuthFailure>(undefined); const [authFailure, setAuthFailure] = React.useState<AadAuthFailure>(undefined);
console.log("Current AAD State", {
isLoggedIn, account, tenantId
});
msalInstance.setActiveAccount(account); msalInstance.setActiveAccount(account);
const login = React.useCallback(async () => { const login = React.useCallback(async (userTriggered: boolean = true) => {
if (!userTriggered) {
console.log("Starting non-interactive login");
// If the user didn't trigger the login, we don't want to pop up the login dialog
await msalInstance.acquireTokenRedirect({
redirectUri: configContext.msalRedirectURI,
scopes: [],
});
return;
}
const response = await msalInstance.loginPopup({ const response = await msalInstance.loginPopup({
redirectUri: configContext.msalRedirectURI, redirectUri: configContext.msalRedirectURI,
scopes: [], scopes: [],
@ -47,13 +60,13 @@ export function useAADAuth(): ReturnType {
setAccount(response.account); setAccount(response.account);
setTenantId(response.tenantId); setTenantId(response.tenantId);
localStorage.setItem("cachedTenantId", response.tenantId); localStorage.setItem("cachedTenantId", response.tenantId);
}, []); }, [setLoggedIn, setAccount, setTenantId]);
const logout = React.useCallback(() => { const logout = React.useCallback(() => {
setLoggedOut(); setLoggedOut();
localStorage.removeItem("cachedTenantId"); localStorage.removeItem("cachedTenantId");
msalInstance.logoutRedirect(); msalInstance.logoutRedirect();
}, []); }, [setLoggedOut]);
const switchTenant = React.useCallback( const switchTenant = React.useCallback(
async (id) => { async (id) => {
@ -66,7 +79,7 @@ export function useAADAuth(): ReturnType {
setAccount(response.account); setAccount(response.account);
localStorage.setItem("cachedTenantId", response.tenantId); localStorage.setItem("cachedTenantId", response.tenantId);
}, },
[account, tenantId], [setTenantId, setAccount],
); );
const acquireTokens = React.useCallback(async () => { const acquireTokens = React.useCallback(async () => {
@ -123,6 +136,19 @@ export function useAADAuth(): ReturnType {
} }
}, [account, tenantId, acquireTokens, authFailure]); }, [account, tenantId, acquireTokens, authFailure]);
React.useEffect(() => {
(async () => {
// If we're on a redirect, handle it
const response = await msalInstance.handleRedirectPromise();
if (response) {
setLoggedIn();
setAccount(response.account);
setTenantId(response.tenantId);
localStorage.setItem("cachedTenantId", response.tenantId);
}
})()
}, [setLoggedIn])
return { return {
account, account,
tenantId, tenantId,