From 35e094868eba7ee83117b53986ea017f22cc307e Mon Sep 17 00:00:00 2001 From: Gina Triolo <51341242+gitri-ms@users.noreply.github.com> Date: Mon, 5 Jun 2023 17:00:29 -0700 Subject: [PATCH] Copilot Chat: Use MSAL active account instead of accounts[0] (#1318) ### Motivation and Context We are currently using `accounts[0]` to access the user's MSAL account information and retrieve tokens in the Copilot Chat frontend. This works fine if only one account is logged in. However, if the user is signed in with multiple accounts, this list is not guaranteed to be in the same order each time (per MSAL documentation [here](https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/accounts.md#notes:~:text=getAllAccounts()%20is%20not%20ordered%20and%20is%20not%20guaranteed%20to%20be%20in%20the%20same%20order%20across%20multiple%20calls)). We need to ensure we are using the currently active account for consistent behavior. ### Description - Set the active account once the user has logged in using `handleRedirectPromise` - Use `instance.getActiveAccount` everywhere we need to access the account --- samples/apps/copilot-chat-app/webapp/src/App.tsx | 6 ++---- .../webapp/src/components/chat/ChatInput.tsx | 6 +++--- .../webapp/src/components/chat/ChatRoom.tsx | 6 +++--- .../webapp/src/components/chat/ChatStatus.tsx | 6 +++--- samples/apps/copilot-chat-app/webapp/src/index.tsx | 6 ++++++ .../copilot-chat-app/webapp/src/libs/auth/AuthHelper.ts | 6 +++--- samples/apps/copilot-chat-app/webapp/src/libs/useChat.ts | 6 +++--- 7 files changed, 23 insertions(+), 19 deletions(-) diff --git a/samples/apps/copilot-chat-app/webapp/src/App.tsx b/samples/apps/copilot-chat-app/webapp/src/App.tsx index 894ae7b695..4bf870ffde 100644 --- a/samples/apps/copilot-chat-app/webapp/src/App.tsx +++ b/samples/apps/copilot-chat-app/webapp/src/App.tsx @@ -3,7 +3,6 @@ import { AuthenticatedTemplate, UnauthenticatedTemplate, - useAccount, useIsAuthenticated, useMsal, } from '@azure/msal-react'; @@ -68,15 +67,14 @@ const App: FC = () => { const { alerts } = useAppSelector((state: RootState) => state.app); const dispatch = useAppDispatch(); - const { instance, accounts, inProgress } = useMsal(); - const account = useAccount(accounts[0] || {}); + const { instance, inProgress } = useMsal(); + const account = instance.getActiveAccount(); const isAuthenticated = useIsAuthenticated(); const chat = useChat(); useEffect(() => { if (isAuthenticated && account && appState === AppState.LoadingChats) { - instance.setActiveAccount(account); // Load all chats from memory async function loadChats() { diff --git a/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatInput.tsx b/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatInput.tsx index 845488e1f4..5d485b29bc 100644 --- a/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatInput.tsx +++ b/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatInput.tsx @@ -1,6 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. -import { useAccount, useMsal } from '@azure/msal-react'; +import { useMsal } from '@azure/msal-react'; import { Button, Spinner, Textarea, makeStyles, shorthands, tokens } from '@fluentui/react-components'; import { AttachRegular, MicRegular, SendRegular } from '@fluentui/react-icons'; import debug from 'debug'; @@ -64,8 +64,8 @@ interface ChatInputProps { export const ChatInput: React.FC = (props) => { const { isTyping, onSubmit } = props; const classes = useClasses(); - const { instance, accounts, inProgress } = useMsal(); - const account = useAccount(accounts[0] || {}); + const { instance, inProgress } = useMsal(); + const account = instance.getActiveAccount(); const dispatch = useAppDispatch(); const [value, setValue] = React.useState(''); const [recognizer, setRecognizer] = React.useState(); diff --git a/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatRoom.tsx b/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatRoom.tsx index 5f2660436e..b93c2f418c 100644 --- a/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatRoom.tsx +++ b/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatRoom.tsx @@ -1,6 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. -import { useAccount, useMsal } from '@azure/msal-react'; +import { useMsal } from '@azure/msal-react'; import { makeStyles, shorthands, tokens } from '@fluentui/react-components'; import debug from 'debug'; import React from 'react'; @@ -56,8 +56,8 @@ export const ChatRoom: React.FC = () => { const messages = conversations[selectedId].messages; const classes = useClasses(); - const { accounts } = useMsal(); - const account = useAccount(accounts[0] || {}); + const { instance } = useMsal(); + const account = instance.getActiveAccount(); const dispatch = useAppDispatch(); const scrollViewTargetRef = React.useRef(null); diff --git a/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatStatus.tsx b/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatStatus.tsx index 1b07ec1325..6f30856ef0 100644 --- a/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatStatus.tsx +++ b/samples/apps/copilot-chat-app/webapp/src/components/chat/ChatStatus.tsx @@ -1,6 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. -import { useAccount, useMsal } from '@azure/msal-react'; +import { useMsal } from '@azure/msal-react'; import { Label, makeStyles } from '@fluentui/react-components'; import React from 'react'; import { Constants } from '../../Constants'; @@ -19,8 +19,8 @@ const useClasses = makeStyles({ export const ChatStatus: React.FC = () => { const classes = useClasses(); - const { accounts } = useMsal(); - const account = useAccount(accounts[0] || {}); + const { instance } = useMsal(); + const account = instance.getActiveAccount(); const { conversations, selectedId } = useAppSelector((state: RootState) => state.conversations); const { users } = conversations[selectedId]; const [typingUserList, setTypingUserList] = React.useState([]); diff --git a/samples/apps/copilot-chat-app/webapp/src/index.tsx b/samples/apps/copilot-chat-app/webapp/src/index.tsx index 119376ff83..bae10a5f7c 100644 --- a/samples/apps/copilot-chat-app/webapp/src/index.tsx +++ b/samples/apps/copilot-chat-app/webapp/src/index.tsx @@ -33,6 +33,12 @@ document.addEventListener('DOMContentLoaded', async () => { var msalInstance: IPublicClientApplication | null = null; if (validEnvFile) { msalInstance = new PublicClientApplication(AuthHelper.msalConfig); + + msalInstance.handleRedirectPromise().then((response) => { + if (response) { + msalInstance?.setActiveAccount(response?.account); + } + }); } root.render( diff --git a/samples/apps/copilot-chat-app/webapp/src/libs/auth/AuthHelper.ts b/samples/apps/copilot-chat-app/webapp/src/libs/auth/AuthHelper.ts index 678bf95bae..94c48a8ae5 100644 --- a/samples/apps/copilot-chat-app/webapp/src/libs/auth/AuthHelper.ts +++ b/samples/apps/copilot-chat-app/webapp/src/libs/auth/AuthHelper.ts @@ -54,14 +54,14 @@ const logoutRequest: EndSessionRequest = { }; const ssoSilentRequest = async (msalInstance: IPublicClientApplication) => { - await msalInstance.ssoSilent({ account: msalInstance.getAllAccounts()[0], scopes: Constants.msal.semanticKernelScopes }); + await msalInstance.ssoSilent({ account: msalInstance.getActiveAccount() || undefined, scopes: Constants.msal.semanticKernelScopes }); }; const loginAsync = async (instance: IPublicClientApplication) => { if (Constants.msal.method === 'redirect') { - await instance.loginRedirect({ account: instance.getAllAccounts()[0], scopes: Constants.msal.semanticKernelScopes }); + await instance.loginRedirect({ scopes: Constants.msal.semanticKernelScopes }); } else { - await instance.loginPopup({ account: instance.getAllAccounts()[0], scopes: Constants.msal.semanticKernelScopes }); + await instance.loginPopup({ scopes: Constants.msal.semanticKernelScopes }); } }; diff --git a/samples/apps/copilot-chat-app/webapp/src/libs/useChat.ts b/samples/apps/copilot-chat-app/webapp/src/libs/useChat.ts index 0c614a2dfd..591a3299d9 100644 --- a/samples/apps/copilot-chat-app/webapp/src/libs/useChat.ts +++ b/samples/apps/copilot-chat-app/webapp/src/libs/useChat.ts @@ -1,6 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. -import { useAccount, useMsal } from '@azure/msal-react'; +import { useMsal } from '@azure/msal-react'; import { Constants } from '../Constants'; import { useAppDispatch, useAppSelector } from '../redux/app/hooks'; import { RootState } from '../redux/app/store'; @@ -33,8 +33,8 @@ import { AuthHeaderTags } from '../redux/features/plugins/PluginsState'; export const useChat = () => { const dispatch = useAppDispatch(); - const { instance, accounts, inProgress } = useMsal(); - const account = useAccount(accounts[0] || {}); + const { instance, inProgress } = useMsal(); + const account = instance.getActiveAccount(); const { conversations } = useAppSelector((state: RootState) => state.conversations); const botService = new BotService(process.env.REACT_APP_BACKEND_URI as string);