diff --git a/ui/src/api/api.tsx b/ui/src/api/api.tsx index 653a2677a..ee509411f 100644 --- a/ui/src/api/api.tsx +++ b/ui/src/api/api.tsx @@ -1,11 +1,14 @@ import Axios from "axios"; import { DataSource, Feature, FeatureLineage, UserRole, Role } from "../models/model"; +import { InteractionRequiredAuthError, PublicClientApplication } from "@azure/msal-browser"; import mockUserRole from "./mock/userrole.json"; +import { getMsalConfig } from "../utils/utils"; const API_ENDPOINT = process.env.REACT_APP_API_ENDPOINT + "/api/v1"; -const token = "mockAppServiceKey"; +const msalInstance = getMsalConfig(); export const fetchDataSources = async (project: string) => { + const token = await getIdToken(msalInstance); return Axios .get(`${ API_ENDPOINT }/projects/${ project }/datasources?code=${ token }`, { headers: {} }) @@ -15,6 +18,7 @@ export const fetchDataSources = async (project: string) => { }; export const fetchProjects = async () => { + const token = await getIdToken(msalInstance); return Axios .get<[]>(`${ API_ENDPOINT }/projects?code=${ token }`, { @@ -26,6 +30,7 @@ export const fetchProjects = async () => { }; export const fetchFeatures = async (project: string, page: number, limit: number, keyword: string) => { + const token = await getIdToken(msalInstance); return Axios .get(`${ API_ENDPOINT }/projects/${ project }/features?code=${ token }`, { @@ -38,6 +43,7 @@ export const fetchFeatures = async (project: string, page: number, limit: number }; export const fetchFeature = async (project: string, featureId: string) => { + const token = await getIdToken(msalInstance); return Axios .get(`${ API_ENDPOINT }/features/${ featureId }?code=${ token }`, {}) .then((response) => { @@ -46,6 +52,7 @@ export const fetchFeature = async (project: string, featureId: string) => { }; export const fetchProjectLineages = async (project: string) => { + const token = await getIdToken(msalInstance); return Axios .get(`${ API_ENDPOINT }/projects/${ project }?code=${ token }`, {}) .then((response) => { @@ -54,6 +61,7 @@ export const fetchProjectLineages = async (project: string) => { }; export const fetchFeatureLineages = async (project: string) => { + const token = await getIdToken(msalInstance); return Axios .get(`${ API_ENDPOINT }/features/lineage/${ project }?code=${ token }`, {}) .then((response) => { @@ -63,8 +71,9 @@ export const fetchFeatureLineages = async (project: string) => { // Following are place-holder code export const createFeature = async (feature: Feature) => { + const token = await getIdToken(msalInstance); return Axios - .post(`${ API_ENDPOINT }/features`, feature, + .post(`${ API_ENDPOINT }/features?code=${ token }`, feature, { headers: { "Content-Type": "application/json;" }, params: {}, @@ -76,8 +85,9 @@ export const createFeature = async (feature: Feature) => { } export const updateFeature = async (feature: Feature, id: string) => { + const token = await getIdToken(msalInstance); feature.guid = id; - return await Axios.put(`${ API_ENDPOINT }/features/${ id }`, feature, + return await Axios.put(`${ API_ENDPOINT }/features/${ id }?code=${ token }`, feature, { headers: { "Content-Type": "application/json;" }, params: {}, @@ -89,8 +99,9 @@ export const updateFeature = async (feature: Feature, id: string) => { }; export const deleteFeature = async (qualifiedName: string) => { + const token = await getIdToken(msalInstance); return await Axios - .delete(`${ API_ENDPOINT }/features/${ qualifiedName }`, + .delete(`${ API_ENDPOINT }/features/${ qualifiedName }?code=${ token }`, { headers: { "Content-Type": "application/json;" }, params: {}, @@ -107,6 +118,7 @@ export const listUserRole = async () => { }; export const getUserRole = async (userName: string) => { + const token = await getIdToken(msalInstance); return await Axios .get(`${ API_ENDPOINT }/user/${userName}/userroles?code=${ token }`, {}) .then((response) => { @@ -115,8 +127,9 @@ export const getUserRole = async (userName: string) => { } export const addUserRole = async (role: Role) => { + const token = await getIdToken(msalInstance); return await Axios - .post(`${ API_ENDPOINT }/user/${role.userName}/userroles/new`, role, + .post(`${ API_ENDPOINT }/user/${role.userName}/userroles/new?code=${ token }`, role, { headers: { "Content-Type": "application/json;" }, params: {}, @@ -128,8 +141,9 @@ export const addUserRole = async (role: Role) => { } export const deleteUserRole = async (role: Role) => { + const token = await getIdToken(msalInstance); return await Axios - .post(`${ API_ENDPOINT }/user/${role.userName}/userroles/delete`, role, + .post(`${ API_ENDPOINT }/user/${role.userName}/userroles/delete?code=${ token }`, role, { headers: { "Content-Type": "application/json;" }, params: {}, @@ -139,3 +153,23 @@ export const deleteUserRole = async (role: Role) => { return error.response; }); } + +export const getIdToken = async( msalInstance: PublicClientApplication ): Promise => { + const activeAccount = msalInstance.getActiveAccount(); // This will only return a non-null value if you have logic somewhere else that calls the setActiveAccount API + const accounts = msalInstance.getAllAccounts(); + const request = { + scopes: ["User.Read"], + account: activeAccount || accounts[0] + }; + // Silently acquire an token for a given set of scopes. Will use cached token if available, otherwise will attempt to acquire a new token from the network via refresh token. + await msalInstance.acquireTokenSilent(request).then(response => { + return response.idToken + }).catch(error => { + if (error instanceof InteractionRequiredAuthError) { + msalInstance.acquireTokenPopup(request).then(response => { + return response.idToken + }); + } + }) + return "" +} \ No newline at end of file diff --git a/ui/src/app.tsx b/ui/src/app.tsx index 2a21eb373..3abae7935 100644 --- a/ui/src/app.tsx +++ b/ui/src/app.tsx @@ -2,7 +2,7 @@ import React from "react"; import { BrowserRouter, Route, Routes } from "react-router-dom"; import { Layout } from "antd"; import { QueryClient, QueryClientProvider } from "react-query"; -import { Configuration, InteractionType, PublicClientApplication, } from "@azure/msal-browser"; +import { InteractionType } from "@azure/msal-browser"; import { MsalAuthenticationTemplate, MsalProvider } from "@azure/msal-react"; import Header from "./components/header/header"; import SideMenu from "./components/sidemenu/siteMenu"; @@ -15,17 +15,11 @@ import Monitoring from "./pages/monitoring/monitoring"; import LineageGraph from "./pages/feature/lineageGraph"; import Management from "./pages/management/management"; import RoleManagement from "./pages/management/roleManagement"; +import { getMsalConfig } from "./utils/utils"; const queryClient = new QueryClient(); -const msalConfig: Configuration = { - auth: { - clientId: process.env.REACT_APP_AAD_APP_CLIENT_ID, - authority: process.env.REACT_APP_AAD_APP_AUTHORITY, - redirectUri: window.location.origin, - }, -}; -const msalClient = new PublicClientApplication(msalConfig); +const msalClient = getMsalConfig(); const App: React.FC = () => { return ( diff --git a/ui/src/utils/utils.tsx b/ui/src/utils/utils.tsx new file mode 100644 index 000000000..8ae041e27 --- /dev/null +++ b/ui/src/utils/utils.tsx @@ -0,0 +1,12 @@ +import { Configuration, PublicClientApplication } from "@azure/msal-browser"; + +export const getMsalConfig = () => { + const msalConfig: Configuration = { + auth: { + clientId: process.env.REACT_APP_AAD_APP_CLIENT_ID, + authority: process.env.REACT_APP_AAD_APP_AUTHORITY, + redirectUri: window.location.origin, + }, + }; + return new PublicClientApplication(msalConfig); +} \ No newline at end of file