Skip to content

Commit

Permalink
add auth
Browse files Browse the repository at this point in the history
  • Loading branch information
devksingh4 committed Mar 28, 2024
1 parent 1f88da6 commit bd5cdc3
Show file tree
Hide file tree
Showing 6 changed files with 451 additions and 28 deletions.
7 changes: 7 additions & 0 deletions admin-api-frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,21 @@
"private": true,
"homepage": "/admin-api",
"dependencies": {
"@azure/msal-browser": "^3.11.1",
"@azure/msal-react": "^2.0.14",
"@nextui-org/react": "^2.2.9",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"@typescript-eslint/parser": "^7.4.0",
"bootstrap": "^5.3.3",
"framer-motion": "^11.0.5",
"react": "^18.2.0",
"react-bootstrap": "^2.10.2",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.3",
"react-scripts": "5.0.1",
"typescript": "^5.4.3",
"web-vitals": "^2.1.4"
},
"scripts": {
Expand Down
54 changes: 37 additions & 17 deletions admin-api-frontend/src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,27 +4,47 @@ import AddRolesForm from "./components/AddRolesForm/index.tsx";
import RemoveRolesForm from "./components/RemoveRolesForm/index.tsx";
import CreateUserForm from "./components/CreateUserForm/index.tsx";
import DeleteUserForm from "./components/DeleteUserForm/index.tsx";

import { MsalProvider } from '@azure/msal-react';
import { NextUIProvider } from "@nextui-org/react";
import { MsalAuthenticationTemplate } from '@azure/msal-react';
import { InteractionType } from '@azure/msal-browser';
import { loginRequest } from "./authConfig";


function App() {
const App = ({ instance }) => {
const authRequest = {
...loginRequest,
};
let acct = instance.getActiveAccount();
if (!acct) {
console.error("Ah fuck!")
return null;
}
console.log(acct);
return (
<NextUIProvider>
<div className="App">
<header className="App-header">
<p>ACM Admin API </p>
<p>Roles and permissions should be comma separated </p>
<div className="Form-container">
<DeleteUserForm />
<GetUserInfoForm />
<RemoveRolesForm />
<AddRolesForm />
<CreateUserForm />
<MsalProvider instance={instance}>
<MsalAuthenticationTemplate
interactionType={InteractionType.Redirect}
authenticationRequest={authRequest}>
<NextUIProvider>
<div className="App">
<header className="App-header">
<p>ACM Admin API</p>
<p>Welcome {acct["name"]}!</p>
<p>Roles and permissions should be comma separated </p>
<div className="Form-container">
<DeleteUserForm />
<GetUserInfoForm />
<RemoveRolesForm />
<AddRolesForm />
<CreateUserForm />
</div>
</header>
</div>
</header>
</div>
</NextUIProvider>
</NextUIProvider>
</MsalAuthenticationTemplate>
</MsalProvider>
);
}
};

export default App;
77 changes: 77 additions & 0 deletions admin-api-frontend/src/authConfig.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
/*
* 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: '393a9b2f-620d-45d5-8b69-f8d36d3e123b', // This is the ONLY mandatory field that you need to supply.
authority: 'https://login.microsoftonline.com/c8d9148f-9a59-4db3-827d-42ea0c2b6e2e/', // Replace the placeholder with your tenant subdomain
redirectUri: '/admin-api/', // You must register this URI on Azure Portal/App Registration. Defaults to window.location.origin
postLogoutRedirectUri: '/', // Indicates the page to navigate after logout.
},
cache: {
cacheLocation: 'localStorage', // 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: {
/**
* Below you can configure MSAL.js logs. For more information, visit:
* https://docs.microsoft.com/azure/active-directory/develop/msal-logging-js
*/
loggerCallback: (level, message, containsPii) => {
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;
}
},
},
},
};

/**
* Add here the endpoints and scopes when obtaining an access token for protected web APIs. For more information, see:
* https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-browser/docs/resources-and-scopes.md
*/
export const protectedResources = {
AdminAPI: {
endpoint: 'https://localhost:44351/api/todolist',
scopes: {
read: ['api://86baa161-ea6e-4c68-a75a-9ff13690f7da/AdminAPI.Write'],
write: ['api://86baa161-ea6e-4c68-a75a-9ff13690f7da/AdminAPI.Write'],
},
},
};

/**
* 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: [...protectedResources.AdminAPI.scopes.read, ...protectedResources.AdminAPI.scopes.write],
};
90 changes: 90 additions & 0 deletions admin-api-frontend/src/hooks/useFetchWithMsal.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
import {
useState,
useCallback,
} from 'react';

import { InteractionType } from '@azure/msal-browser';
import { useMsal, useMsalAuthentication } from "@azure/msal-react";

/**
* Custom hook to call a web API using bearer token obtained from MSAL
* @param {PopupRequest} msalRequest
* @returns
*/
const useFetchWithMsal = (msalRequest) => {
const { instance } = useMsal();
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState(null);
const [data, setData] = useState(null);

const { result, error: msalError } = useMsalAuthentication(InteractionType.Popup, {
...msalRequest,
account: instance.getActiveAccount(),
redirectUri: '/redirect'
});

/**
* Execute a fetch request with the given options
* @param {string} method: GET, POST, PUT, DELETE
* @param {String} endpoint: The endpoint to call
* @param {Object} data: The data to send to the endpoint, if any
* @returns JSON response
*/
const execute = async (method, endpoint, data = null) => {
if (msalError) {
setError(msalError);
return;
}

if (result) {
try {
let response = null;

const headers = new Headers();
const bearer = `Bearer ${result.accessToken}`;
headers.append("Authorization", bearer);

if (data) headers.append('Content-Type', 'application/json');

let options = {
method: method,
headers: headers,
body: data ? JSON.stringify(data) : null,
};

setIsLoading(true);
response = (await fetch(endpoint, options));

if ((response.status === 200 || response.status === 201)) {
let responseData = response;

try {
responseData = await response.json();
} catch (error) {
console.log(error);
} finally {
setData(responseData);
setIsLoading(false);
return responseData;
}
}

setIsLoading(false);
return response;
} catch (e) {
setError(e);
setIsLoading(false);
throw e;
}
}
};

return {
isLoading,
error,
data,
execute: useCallback(execute, [result, msalError]), // to avoid infinite calls when inside a `useEffect`
};
};

export default useFetchWithMsal;
45 changes: 34 additions & 11 deletions admin-api-frontend/src/index.js
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
import React from 'react';
import ReactDOM from 'react-dom/client';
import './index.css';
import { BrowserRouter } from 'react-router-dom';
import { PublicClientApplication, EventType } from '@azure/msal-browser';

import { msalConfig } from './authConfig.js';
import App from './App';
import reportWebVitals from './reportWebVitals';

import 'bootstrap/dist/css/bootstrap.min.css';

/**
* MSAL should be instantiated outside of the component tree to prevent it from being re-instantiated on re-renders.
* For more, visit: https://github.com/AzureAD/microsoft-authentication-library-for-js/blob/dev/lib/msal-react/docs/getting-started.md
*/
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]);
}

// Optional - This will update account state if a user signs in from another tab or window
msalInstance.enableAccountStorageEvents();

// Listen for sign-in event and set active account
msalInstance.addEventCallback((event) => {
if (event.eventType === EventType.LOGIN_SUCCESS && event.payload.account) {
const account = event.payload.account;
msalInstance.setActiveAccount(account);
}
});

const root = ReactDOM.createRoot(document.getElementById('root'));

root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);

// If you want to start measuring performance in your app, pass a function
// to log results (for example: reportWebVitals(console.log))
// or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals
reportWebVitals();
<BrowserRouter>
<App instance={msalInstance} />
</BrowserRouter>
);
Loading

0 comments on commit bd5cdc3

Please sign in to comment.