mirror of
				https://github.com/open-metadata/OpenMetadata.git
				synced 2025-10-26 08:13:11 +00:00 
			
		
		
		
	fix(ui): MSAL popup auth issue (#18125)
(cherry picked from commit e44c8d4f3143831c0aa95fe31d89f93b7b0da638)
This commit is contained in:
		
							parent
							
								
									6a36355d3e
								
							
						
					
					
						commit
						a1a9b91a1e
					
				| @ -45,8 +45,8 @@ | ||||
|     "@ant-design/icons": "^4.7.0", | ||||
|     "@apidevtools/json-schema-ref-parser": "^9.0.9", | ||||
|     "@auth0/auth0-react": "^1.9.0", | ||||
|     "@azure/msal-browser": "^3.10.0", | ||||
|     "@azure/msal-react": "^2.0.12", | ||||
|     "@azure/msal-browser": "^3.25.0", | ||||
|     "@azure/msal-react": "^2.1.1", | ||||
|     "@dagrejs/dagre": "^1.1.2", | ||||
|     "@deuex-solutions/react-tour": "^1.2.6", | ||||
|     "@fontsource/poppins": "^5.0.0", | ||||
|  | ||||
| @ -12,27 +12,29 @@ | ||||
|  */ | ||||
| 
 | ||||
| import { | ||||
|   AuthenticationResult, | ||||
|   InteractionRequiredAuthError, | ||||
|   InteractionStatus, | ||||
| } from '@azure/msal-browser'; | ||||
| import { useAccount, useMsal } from '@azure/msal-react'; | ||||
| import { AxiosError } from 'axios'; | ||||
| import { get } from 'lodash'; | ||||
| import React, { | ||||
|   forwardRef, | ||||
|   Fragment, | ||||
|   ReactNode, | ||||
|   useEffect, | ||||
|   useImperativeHandle, | ||||
| } from 'react'; | ||||
| import { toast } from 'react-toastify'; | ||||
| import { useApplicationStore } from '../../../hooks/useApplicationStore'; | ||||
| import { msalLoginRequest } from '../../../utils/AuthProvider.util'; | ||||
| import { | ||||
|   msalLoginRequest, | ||||
|   parseMSALResponse, | ||||
| } from '../../../utils/AuthProvider.util'; | ||||
| import { getPopupSettingLink } from '../../../utils/BrowserUtils'; | ||||
| import { Transi18next } from '../../../utils/CommonUtils'; | ||||
| import Loader from '../../common/Loader/Loader'; | ||||
| import { | ||||
|   AuthenticatorRef, | ||||
|   OidcUser, | ||||
|   UserProfile, | ||||
| } from '../AuthProviders/AuthProvider.interface'; | ||||
| 
 | ||||
| interface Props { | ||||
| @ -47,36 +49,10 @@ const MsalAuthenticator = forwardRef<AuthenticatorRef, Props>( | ||||
|     { children, onLoginSuccess, onLogoutSuccess, onLoginFailure }: Props, | ||||
|     ref | ||||
|   ) => { | ||||
|     const { setOidcToken } = useApplicationStore(); | ||||
|     const { instance, accounts } = useMsal(); | ||||
|     const { instance, accounts, inProgress } = useMsal(); | ||||
|     const account = useAccount(accounts[0] || {}); | ||||
| 
 | ||||
|     const parseResponse = (response: AuthenticationResult): OidcUser => { | ||||
|       // Call your API with the access token and return the data you need to save in state
 | ||||
|       const { idToken, scopes, account } = response; | ||||
| 
 | ||||
|       const user = { | ||||
|         id_token: idToken, | ||||
|         scope: scopes.join(), | ||||
|         profile: { | ||||
|           email: get(account, 'idTokenClaims.email', ''), | ||||
|           name: account?.name || '', | ||||
|           picture: '', | ||||
|           preferred_username: get( | ||||
|             account, | ||||
|             'idTokenClaims.preferred_username', | ||||
|             '' | ||||
|           ), | ||||
|           sub: get(account, 'idTokenClaims.sub', ''), | ||||
|         } as UserProfile, | ||||
|       }; | ||||
| 
 | ||||
|       setOidcToken(idToken); | ||||
| 
 | ||||
|       return user; | ||||
|     }; | ||||
| 
 | ||||
|     const handleOnLogoutSuccess = () => { | ||||
|     const handleOnLogoutSuccess = async () => { | ||||
|       for (const key in localStorage) { | ||||
|         if (key.includes('-login.windows.net-') || key.startsWith('msal.')) { | ||||
|           localStorage.removeItem(key); | ||||
| @ -87,9 +63,8 @@ const MsalAuthenticator = forwardRef<AuthenticatorRef, Props>( | ||||
| 
 | ||||
|     const login = async () => { | ||||
|       try { | ||||
|         const response = await instance.loginPopup(msalLoginRequest); | ||||
| 
 | ||||
|         onLoginSuccess(parseResponse(response)); | ||||
|         // Use login with redirect to avoid popup issue with maximized browser window
 | ||||
|         await instance.loginRedirect(); | ||||
|       } catch (error) { | ||||
|         onLoginFailure(error as AxiosError); | ||||
|       } | ||||
| @ -109,7 +84,7 @@ const MsalAuthenticator = forwardRef<AuthenticatorRef, Props>( | ||||
|       try { | ||||
|         const response = await instance.ssoSilent(tokenRequest); | ||||
| 
 | ||||
|         return parseResponse(response); | ||||
|         return parseMSALResponse(response); | ||||
|       } catch (error) { | ||||
|         if ( | ||||
|           error instanceof InteractionRequiredAuthError && | ||||
| @ -138,7 +113,7 @@ const MsalAuthenticator = forwardRef<AuthenticatorRef, Props>( | ||||
|               throw e; | ||||
|             }); | ||||
| 
 | ||||
|           return parseResponse(response); | ||||
|           return parseMSALResponse(response); | ||||
|         } else { | ||||
|           // eslint-disable-next-line no-console
 | ||||
|           console.error(error); | ||||
| @ -160,6 +135,32 @@ const MsalAuthenticator = forwardRef<AuthenticatorRef, Props>( | ||||
|       renewIdToken: renewIdToken, | ||||
|     })); | ||||
| 
 | ||||
|     // Need to capture redirect and parse ID token
 | ||||
|     // Call login success callback
 | ||||
|     const handleRedirect = async () => { | ||||
|       try { | ||||
|         const response = await instance.handleRedirectPromise(); | ||||
| 
 | ||||
|         if (response) { | ||||
|           const user = parseMSALResponse(response); | ||||
| 
 | ||||
|           onLoginSuccess(user); | ||||
|         } | ||||
|       } catch (error) { | ||||
|         onLoginFailure(error as AxiosError); | ||||
|       } | ||||
|     }; | ||||
| 
 | ||||
|     // To add redirect callback
 | ||||
|     useEffect(() => { | ||||
|       instance && handleRedirect(); | ||||
|     }, [instance]); | ||||
| 
 | ||||
|     // Show loader until the interaction is completed
 | ||||
|     if (inProgress !== InteractionStatus.None) { | ||||
|       return <Loader />; | ||||
|     } | ||||
| 
 | ||||
|     return <Fragment>{children}</Fragment>; | ||||
|   } | ||||
| ); | ||||
|  | ||||
| @ -459,16 +459,21 @@ export const AuthProvider = ({ | ||||
|     } | ||||
|   }, [location.pathname, storeRedirectPath]); | ||||
| 
 | ||||
|   const updateAuthInstance = (configJson: AuthenticationConfiguration) => { | ||||
|   const updateAuthInstance = async ( | ||||
|     configJson: AuthenticationConfiguration | ||||
|   ) => { | ||||
|     const { provider, ...otherConfigs } = configJson; | ||||
|     switch (provider) { | ||||
|       case AuthProviderEnum.Azure: | ||||
|         { | ||||
|           setMsalInstance( | ||||
|             new PublicClientApplication( | ||||
|           const instance = new PublicClientApplication( | ||||
|             otherConfigs as unknown as Configuration | ||||
|             ) | ||||
|           ); | ||||
| 
 | ||||
|           // Need to initialize the instance before setting it
 | ||||
|           await instance.initialize(); | ||||
| 
 | ||||
|           setMsalInstance(instance); | ||||
|         } | ||||
| 
 | ||||
|         break; | ||||
|  | ||||
| @ -12,6 +12,7 @@ | ||||
|  */ | ||||
| 
 | ||||
| import { | ||||
|   AuthenticationResult, | ||||
|   BrowserCacheLocation, | ||||
|   Configuration, | ||||
|   PopupRequest, | ||||
| @ -32,6 +33,7 @@ import { | ||||
|   ClientType, | ||||
| } from '../generated/configuration/authenticationConfiguration'; | ||||
| import { AuthProvider } from '../generated/settings/settings'; | ||||
| import { useApplicationStore } from '../hooks/useApplicationStore'; | ||||
| import { isDev } from './EnvironmentUtils'; | ||||
| 
 | ||||
| const cookieStorage = new CookieStorage(); | ||||
| @ -412,3 +414,26 @@ export const prepareUserProfileFromClaims = ({ | ||||
| 
 | ||||
|   return newUser; | ||||
| }; | ||||
| 
 | ||||
| // Responsible for parsing the response from MSAL AuthenticationResult
 | ||||
| export const parseMSALResponse = (response: AuthenticationResult): OidcUser => { | ||||
|   // Call your API with the access token and return the data you need to save in state
 | ||||
|   const { idToken, scopes, account } = response; | ||||
|   const { setOidcToken } = useApplicationStore.getState(); | ||||
| 
 | ||||
|   const user = { | ||||
|     id_token: idToken, | ||||
|     scope: scopes.join(), | ||||
|     profile: { | ||||
|       email: get(account, 'idTokenClaims.email', ''), | ||||
|       name: account?.name ?? '', | ||||
|       picture: '', | ||||
|       preferred_username: get(account, 'idTokenClaims.preferred_username', ''), | ||||
|       sub: get(account, 'idTokenClaims.sub', ''), | ||||
|     } as UserProfile, | ||||
|   }; | ||||
| 
 | ||||
|   setOidcToken(idToken); | ||||
| 
 | ||||
|   return user; | ||||
| }; | ||||
|  | ||||
| @ -160,22 +160,22 @@ | ||||
|     promise-polyfill "^8.2.1" | ||||
|     unfetch "^4.2.0" | ||||
| 
 | ||||
| "@azure/msal-browser@^3.10.0": | ||||
|   version "3.10.0" | ||||
|   resolved "https://registry.yarnpkg.com/@azure/msal-browser/-/msal-browser-3.10.0.tgz#8925659e8d1a4bd21e389cca4683eb52658c778e" | ||||
|   integrity sha512-mnmi8dCXVNZI+AGRq0jKQ3YiodlIC4W9npr6FCB9WN6NQT+6rq+cIlxgUb//BjLyzKsnYo+i4LROGeMyU+6v1A== | ||||
| "@azure/msal-browser@^3.25.0": | ||||
|   version "3.25.0" | ||||
|   resolved "https://registry.yarnpkg.com/@azure/msal-browser/-/msal-browser-3.25.0.tgz#7ce0949977bc9e0c58319f7090c44fe5537104d4" | ||||
|   integrity sha512-a0Y7pmSy8SC1s9bvwr+REvyAA1nQcITlZvkElM2gNUPYFTTNUTEdcpg73TmawNucyMdZ9xb/GFcuhrLOqYAzwg== | ||||
|   dependencies: | ||||
|     "@azure/msal-common" "14.7.1" | ||||
|     "@azure/msal-common" "14.15.0" | ||||
| 
 | ||||
| "@azure/msal-common@14.7.1": | ||||
|   version "14.7.1" | ||||
|   resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-14.7.1.tgz#b13443fbacc87ce2019a91e81a6582ea73847c75" | ||||
|   integrity sha512-v96btzjM7KrAu4NSEdOkhQSTGOuNUIIsUdB8wlyB9cdgl5KqEKnTonHUZ8+khvZ6Ap542FCErbnTyDWl8lZ2rA== | ||||
| "@azure/msal-common@14.15.0": | ||||
|   version "14.15.0" | ||||
|   resolved "https://registry.yarnpkg.com/@azure/msal-common/-/msal-common-14.15.0.tgz#0e27ac0bb88fe100f4f8d1605b64d5c268636a55" | ||||
|   integrity sha512-ImAQHxmpMneJ/4S8BRFhjt1MZ3bppmpRPYYNyzeQPeFN288YKbb8TmmISQEbtfkQ1BPASvYZU5doIZOPBAqENQ== | ||||
| 
 | ||||
| "@azure/msal-react@^2.0.12": | ||||
|   version "2.0.12" | ||||
|   resolved "https://registry.yarnpkg.com/@azure/msal-react/-/msal-react-2.0.12.tgz#f33c84a57663307fcf3707ca17d9ce2d7934a048" | ||||
|   integrity sha512-23HKdajBWQ5SSzcwwFKHAWaOCpq4UCthoOBgKpab3UoHx0OuFMQiq6CrNBzBKtBFdyxCjadBGzWshRgl0Nvk1g== | ||||
| "@azure/msal-react@^2.1.1": | ||||
|   version "2.1.1" | ||||
|   resolved "https://registry.yarnpkg.com/@azure/msal-react/-/msal-react-2.1.1.tgz#d61cd8698d3963ffff685377ece487d7ad5bc111" | ||||
|   integrity sha512-XOBgAR0fbkfUUkQZyhIlwAZiy8WIzNWxAAFacJgyL8LiE2Y5pgM9var9X0Jh7Oz/1Oy5lhTQDtCYWSMHmTZ84Q== | ||||
| 
 | ||||
| "@babel/code-frame@7.12.11": | ||||
|   version "7.12.11" | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Chirag Madlani
						Chirag Madlani