Refactoring
This commit is contained in:
@@ -1,37 +1,27 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { ISiteNavProps } from './ISiteNavProps';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
import { useAppContext } from '../../hooks/appContext/UseAppContext';
|
||||
import {
|
||||
Avatar,
|
||||
Button,
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Navbar,
|
||||
PlaneIcon,
|
||||
SignOutIcon,
|
||||
Spinner,
|
||||
Typography
|
||||
} from '@noahspan/noahspan-components';
|
||||
import { useIsAuthenticated, useMsal } from '@azure/msal-react';
|
||||
import { InteractionStatus } from '@azure/msal-browser';
|
||||
import { useAccessToken } from '../../hooks/accessToken/UseAcessToken';
|
||||
import { useHttpClient } from '../../hooks/httpClient/UseHttpClient';
|
||||
import { AxiosInstance, AxiosResponse } from 'axios';
|
||||
import { User } from '@microsoft/microsoft-graph-types';
|
||||
import { EventMessage, EventPayload, EventType } from '@azure/msal-browser';
|
||||
|
||||
type EventPayloadExtended = EventPayload & { accessToken: string };
|
||||
import { loginRequest } from '../../hooks/auth/authConfig';
|
||||
|
||||
const SiteNav: React.FC<unknown> = () => {
|
||||
const [loading, setLoading] = useState<boolean>(false);
|
||||
const [userPhoto, setUserPhoto] = useState<string>();
|
||||
const httpClient: AxiosInstance = useHttpClient();
|
||||
const appContext = useAppContext();
|
||||
const isAuthenticated = useIsAuthenticated();
|
||||
const { getAccessToken } = useAccessToken();
|
||||
const { inProgress, instance } = useMsal();
|
||||
const { instance } = useMsal();
|
||||
const navigate = useNavigate();
|
||||
const pages = [
|
||||
{
|
||||
@@ -43,28 +33,23 @@ const SiteNav: React.FC<unknown> = () => {
|
||||
url: '/pilots'
|
||||
}
|
||||
];
|
||||
const handleSignIn = async () => {
|
||||
// instance.handleRedirectPromise().then((response) => {
|
||||
// if (response) {
|
||||
// console.log(response)
|
||||
// console.log('successful login', response);
|
||||
// } else {
|
||||
// instance.loginRedirect({
|
||||
// scopes: [`api://${import.meta.env.VITE_CLIENT_ID}/user_impersonation`]
|
||||
// });
|
||||
// }
|
||||
// }).catch((error) => {
|
||||
// console.log(error);
|
||||
// })
|
||||
try {
|
||||
await instance.loginPopup();
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
const handleSignOut = () => {
|
||||
instance.logoutRedirect();
|
||||
|
||||
const handleSignInRedirect = () => {
|
||||
instance
|
||||
.loginRedirect({
|
||||
...loginRequest,
|
||||
prompt: 'create',
|
||||
})
|
||||
.catch((error) => console.log(error));
|
||||
};
|
||||
|
||||
const handleSignOutRedirect = () => {
|
||||
instance.logoutRedirect({
|
||||
postLogoutRedirectUri: '/'
|
||||
})
|
||||
window.location.reload();
|
||||
}
|
||||
|
||||
const getUserProfile = async (accessToken: string): Promise<User> => {
|
||||
try {
|
||||
const response: AxiosResponse = await httpClient.get(`api/userProfile`, {
|
||||
@@ -79,6 +64,7 @@ const SiteNav: React.FC<unknown> = () => {
|
||||
throw new Error();
|
||||
}
|
||||
};
|
||||
|
||||
const getUserPhoto = async (accessToken: string): Promise<string> => {
|
||||
try {
|
||||
const response: AxiosResponse = await httpClient.get(`api/userPhoto`, {
|
||||
@@ -103,7 +89,7 @@ const SiteNav: React.FC<unknown> = () => {
|
||||
|
||||
const Settings = () => {
|
||||
return (
|
||||
<MenuItem onClick={handleSignOut}>
|
||||
<MenuItem onClick={handleSignOutRedirect}>
|
||||
<SignOutIcon />
|
||||
<Typography sx={{ marginLeft: '10px', textAlign: 'center' }}>
|
||||
Sign Out
|
||||
@@ -112,46 +98,9 @@ const SiteNav: React.FC<unknown> = () => {
|
||||
);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const callback = instance.addEventCallback(
|
||||
async (message: EventMessage) => {
|
||||
if (message.eventType === EventType.LOGIN_SUCCESS) {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const eventPayload: EventPayloadExtended =
|
||||
message.payload as EventPayloadExtended;
|
||||
const userProfile: User = await getUserProfile(
|
||||
eventPayload.accessToken
|
||||
);
|
||||
const userPhoto = await getUserPhoto(eventPayload.accessToken);
|
||||
|
||||
appContext.dispatch({
|
||||
type: 'SET_USER_PROFILE',
|
||||
payload: userProfile
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
return () => {
|
||||
if (callback) {
|
||||
instance.removeEventCallback(callback);
|
||||
appContext.dispatch({ type: 'SET_USER_PROFILE', payload: {} });
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
const setUserProfile = async () => {
|
||||
try {
|
||||
setLoading(true);
|
||||
|
||||
const accessToken: string = await getAccessToken();
|
||||
const userProfile = await getUserProfile(accessToken);
|
||||
const userPhoto = await getUserPhoto(accessToken);
|
||||
@@ -164,8 +113,6 @@ const SiteNav: React.FC<unknown> = () => {
|
||||
});
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
@@ -180,7 +127,7 @@ const SiteNav: React.FC<unknown> = () => {
|
||||
return (
|
||||
<Navbar
|
||||
handlePageClick={handlePageClick}
|
||||
handleSignIn={handleSignIn}
|
||||
handleSignIn={handleSignInRedirect}
|
||||
isAuthenticated={isAuthenticated}
|
||||
logo={<PlaneIcon size="2x" />}
|
||||
pages={pages}
|
||||
|
||||
7
app/src/hooks/auth/UseAuthProvider.tsx
Normal file
7
app/src/hooks/auth/UseAuthProvider.tsx
Normal file
@@ -0,0 +1,7 @@
|
||||
import { AuthProvider } from "./authProvider"
|
||||
|
||||
export const useAuthProvider = () => {
|
||||
return {
|
||||
AuthProvider
|
||||
}
|
||||
}
|
||||
70
app/src/hooks/auth/authConfig.ts
Normal file
70
app/src/hooks/auth/authConfig.ts
Normal file
@@ -0,0 +1,70 @@
|
||||
/*
|
||||
* Copyright (c) Microsoft Corporation. All rights reserved.
|
||||
* Licensed under the MIT License.
|
||||
*/
|
||||
|
||||
import { LogLevel } from '@azure/msal-browser';
|
||||
|
||||
/**
|
||||
* Configuration object to be passed to MSAL instance on creation.
|
||||
* For a full list of MSAL.js configuration parameters, visit:
|
||||
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/configuration.md
|
||||
*/
|
||||
|
||||
export const msalConfig = {
|
||||
auth: {
|
||||
clientId: import.meta.env.VITE_CLIENT_ID, // This is the ONLY mandatory field that you need to supply.
|
||||
authority: `https://login.microsoftonline.com/${import.meta.env.VITE_TENANT_ID}`, // Replace the placeholder with your tenant subdomain
|
||||
redirectUri: import.meta.env.VITE_REDIRECT_URL, // Points to window.location.origin. You must register this URI on Microsoft Entra admin center/App Registration.
|
||||
postLogoutRedirectUri: '/', // Indicates the page to navigate after logout.
|
||||
navigateToLoginRequestUrl: false, // If "true", will navigate back to the original request location before processing the auth code response.
|
||||
},
|
||||
cache: {
|
||||
cacheLocation: 'sessionStorage', // Configures cache location. "sessionStorage" is more secure, but "localStorage" gives you SSO between tabs.
|
||||
storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
|
||||
},
|
||||
system: {
|
||||
loggerOptions: {
|
||||
loggerCallback: (level: any, message: any, containsPii: any) => {
|
||||
if (containsPii) {
|
||||
return;
|
||||
}
|
||||
switch (level) {
|
||||
case LogLevel.Error:
|
||||
console.error(message);
|
||||
return;
|
||||
case LogLevel.Info:
|
||||
console.info(message);
|
||||
return;
|
||||
case LogLevel.Verbose:
|
||||
console.debug(message);
|
||||
return;
|
||||
case LogLevel.Warning:
|
||||
console.warn(message);
|
||||
return;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* Scopes you add here will be prompted for user consent during sign-in.
|
||||
* By default, MSAL.js will add OIDC scopes (openid, profile, email) to any login request.
|
||||
* For more information about OIDC scopes, visit:
|
||||
* https://docs.microsoft.com/en-us/azure/active-directory/develop/v2-permissions-and-consent#openid-connect-scopes
|
||||
*/
|
||||
export const loginRequest = {
|
||||
scopes: [`api://${import.meta.env.VITE_CLIENT_ID}/user_impersonation`]
|
||||
};
|
||||
|
||||
/**
|
||||
* An optional silentRequest object can be used to achieve silent SSO
|
||||
* between applications by providing a "login_hint" property.
|
||||
*/
|
||||
// export const silentRequest = {
|
||||
// scopes: ["openid", "profile"],
|
||||
// loginHint: "example@domain.net"
|
||||
// };
|
||||
32
app/src/hooks/auth/authProvider.tsx
Normal file
32
app/src/hooks/auth/authProvider.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { AuthenticationResult, EventType, PublicClientApplication } from "@azure/msal-browser";
|
||||
import { ReactNode } from "react";
|
||||
import { msalConfig } from "./authConfig";
|
||||
import { MsalProvider } from "@azure/msal-react";
|
||||
|
||||
interface AuthProviderProps {
|
||||
children: ReactNode
|
||||
}
|
||||
|
||||
export const AuthProvider = ({ children }: AuthProviderProps ) => {
|
||||
const msalInstance = new PublicClientApplication(msalConfig);
|
||||
|
||||
// Default to using the first account if no account is active on page load
|
||||
if (!msalInstance.getActiveAccount() && msalInstance.getAllAccounts().length > 0) {
|
||||
// Account selection logic is app dependent. Adjust as needed for different use cases.
|
||||
msalInstance.setActiveAccount(msalInstance.getAllAccounts()[0]);
|
||||
}
|
||||
|
||||
// Listen for sign-in event and set active account
|
||||
msalInstance.addEventCallback((event) => {
|
||||
const authenticationResult = event.payload as AuthenticationResult;
|
||||
const account = authenticationResult?.account;
|
||||
|
||||
if (event.eventType === EventType.LOGIN_SUCCESS && account) {
|
||||
msalInstance.setActiveAccount(account);
|
||||
}
|
||||
});
|
||||
|
||||
return <MsalProvider instance={msalInstance}>
|
||||
{children}
|
||||
</MsalProvider>
|
||||
}
|
||||
@@ -5,27 +5,21 @@ import AppContextProvider from './context/appContext/AppContextProvider.tsx';
|
||||
import { BrowserRouter } from 'react-router-dom';
|
||||
import './index.css';
|
||||
import '@noahspan/noahspan-components/noahspan-components.css';
|
||||
import { PublicClientApplication } from '@azure/msal-browser';
|
||||
import { EventMessage, EventPayload, EventType, LogLevel, PublicClientApplication } from '@azure/msal-browser';
|
||||
import { MsalProvider } from '@azure/msal-react';
|
||||
import { useAuthProvider } from './hooks/auth/UseAuthProvider.tsx';
|
||||
|
||||
const msalInstance: PublicClientApplication = new PublicClientApplication({
|
||||
auth: {
|
||||
clientId: import.meta.env.VITE_CLIENT_ID,
|
||||
authority: `https://login.microsoftonline.com/${import.meta.env.VITE_TENANT_ID}`,
|
||||
redirectUri: import.meta.env.VITE_REDIRECT_URL
|
||||
}
|
||||
});
|
||||
const { AuthProvider } = useAuthProvider();
|
||||
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<AuthProvider>
|
||||
<AppContextProvider>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</AppContextProvider>
|
||||
</AuthProvider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
|
||||
msalInstance.initialize().then(() => {
|
||||
ReactDOM.createRoot(document.getElementById('root')!).render(
|
||||
<React.StrictMode>
|
||||
<MsalProvider instance={msalInstance}>
|
||||
<AppContextProvider>
|
||||
<BrowserRouter>
|
||||
<App />
|
||||
</BrowserRouter>
|
||||
</AppContextProvider>
|
||||
</MsalProvider>
|
||||
</React.StrictMode>
|
||||
);
|
||||
});
|
||||
|
||||
@@ -8,55 +8,66 @@ locals {
|
||||
container_app_app_container_image = {
|
||||
dev = "noahspan/flying-app:v0.0.1"
|
||||
test = "noahspan/flying-app:v0.0.1"
|
||||
prod = "noahspan/flying-app:v0.0.1"
|
||||
}
|
||||
|
||||
container_app_app_container_name = {
|
||||
dev = "flying-app-dev"
|
||||
test = "flying-app-test"
|
||||
prod = "flying-app-prod"
|
||||
}
|
||||
|
||||
container_app_api_name = {
|
||||
dev = "flying-api-dev"
|
||||
test = "flying-api-test"
|
||||
prod = "flying-api-prod"
|
||||
}
|
||||
|
||||
container_app_api_container_image = {
|
||||
dev = "noahspan/flying-api:v0.0.1"
|
||||
test = "noahspan/flying-api:v0.0.1"
|
||||
prod = "noahspan/flying-api:v0.0.1"
|
||||
}
|
||||
|
||||
container_app_api_container_name = {
|
||||
dev = "flying-api-dev"
|
||||
test = "flying-api-test"
|
||||
prod = "flying-api-prod"
|
||||
}
|
||||
|
||||
container_app_api_dapr_app_id = {
|
||||
dev = "flyingapidev"
|
||||
test = "flyingapitest"
|
||||
prod = "flyingpapiprod"
|
||||
}
|
||||
|
||||
container_app_environment_name = {
|
||||
dev = "flying-dev"
|
||||
test = "flying-test"
|
||||
prod = "flying-prod"
|
||||
}
|
||||
|
||||
logbook_feature_flag_active = {
|
||||
dev = "true"
|
||||
test = "true"
|
||||
prod = "true"
|
||||
}
|
||||
|
||||
log_analytics_workspace_name = {
|
||||
dev = "flying-log-analytics-workspace-dev"
|
||||
test = "flying-log-analytics-workspace-test"
|
||||
prod = "flying-log-analytics-workspace-prod"
|
||||
}
|
||||
|
||||
pilots_feature_flag_active = {
|
||||
dev = "true"
|
||||
test = "true"
|
||||
prod = "true"
|
||||
}
|
||||
|
||||
storage_account_name = {
|
||||
dev = "noahspanflyingdev"
|
||||
test = "noahspanflyingtest"
|
||||
prod = "noahspanflyingprod"
|
||||
}
|
||||
}
|
||||
@@ -30,10 +30,6 @@ output "container_app_environment_name" {
|
||||
value = local.container_app_environment_name[var.environment]
|
||||
}
|
||||
|
||||
output "key_vault_name" {
|
||||
value = local.key_vault_name[var.environment]
|
||||
}
|
||||
|
||||
output "logbook_feature_flag_active" {
|
||||
value = local.logbook_feature_flag_active[var.environment]
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user