React.JS + MSAL with ASP.NET Core to use Azure AD with User & Role - Part 3


This is the part 3 and the last part of this blog series. This is where we setup and configure our frontend React.JS application to properly connect to the secured endpoints we created from the previous part. We'll also create and configure the app registration for our React.js in Azure AD.

Mar. 28, 2022
Part Series

React.JS + MSAL with ASP.NET Core to use Azure AD with User & Role - Part 1

React.JS + MSAL with ASP.NET Core to use Azure AD with User & Role - Part 2

Part 3 - Setup React.js to Properly Connect to the Backend Endpoints
1. Configure App Registration in Azure AD.

1.1 Follow the Part 1 step 1 to create the app registration. But for this, we create the app registration for our react.js application.

The supported account is still depends on what type that you prefer.

2. Select the tokens you would like to be issued by the authorization endpoint

2.1 Open the app registration for frontend, go to Authentication and check the Access Token and ID Tokens (ID Tokens in case for implicit and hybrid flows). Then hit Save

3. Add user via Enterprise Applications Page

3.1 Go back to your Azure AD, click the Enterprise applications and choose your application. Once it's open, then choose the Users and groups

3.2 Add user/group then select the users that you want to have the access to our frontend application.

4. Go your React.js app and run this command to install the following packages.
npm i @azure/msal-browser @azure/msal-react axios bulma

axios & bulma are only optional packages.

5. Create authConfig file
        import { LogLevel } from "@azure/msal-browser";

export const msalConfig = {
    auth: {
      clientId: "PUT_YOUR_CLIENT_ID_HERE", // Client ID 
      authority: 'https://login.microsoftonline.com/PUT_YOUR_TENANT_ID_HERE', // Tenant ID of the React.JS App Registration
      redirectUri: "http://localhost:3000/",
    },
    cache: {
      cacheLocation: "sessionStorage", // This configures where your cache will be stored
      storeAuthStateInCookie: false, // Set this to "true" if you are having issues on IE11 or Edge
    },
    system: {
      loggerOptions: {
        loggerCallback: (level: any, message: string, 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;
            }
          },
        },
      },
    };
  
// Can be found in the API Permissions of the ASP.NET Web API
export const loginApiRequest = {
  scopes: ["api://PUT_YOUR_WEBAPI_SCOPE_HERE/api.scope"],
};
      

You must need to update these.

  • clientId
  • authority
  • loginApiRequest
  • 6. Update the index.ts or index.js
            import React from "react";
    import ReactDOM from "react-dom";
    import App from "./App.tsx";
    import { PublicClientApplication } from "@azure/msal-browser";
    import { MsalProvider } from "@azure/msal-react";
    import { msalConfig, loginApiRequest } from "./authConfig";
    import axios from "axios";
    import reportWebVitals from "./reportWebVitals";
    import 'bulma/css/bulma.min.css';
    
    /**
     * Initialize a PublicClientApplication instance which is provided to the MsalProvider component
     * We recommend initializing this outside of your root component to ensure it is not re-initialized on re-renders
     */
    const msalInstance = new PublicClientApplication(msalConfig);
    
    axios.defaults.baseURL = "https://localhost:7002/api/";
    axios.interceptors.request.use(
      async (response) => {
        const account = msalInstance.getAllAccounts()[0];
        const msalResponse = await msalInstance.acquireTokenSilent({
          ...loginApiRequest,
          account: account,
        });
        response.headers.common.Authorization = `Bearer ${ msalResponse.accessToken }`;
        return response;
      },
      (err) => {
        return Promise.reject(err);
      }
    );
    /**
     * We recommend wrapping most or all of your components in the MsalProvider component. It's best to render the MsalProvider as close to the root as possible.
     */
    ReactDOM.render(
      <React.StrictMode>
        <MsalProvider instance={msalInstance}>
          <App />
        </MsalProvider>
      </React.StrictMode>,
      document.getElementById("root")
    );
    
    reportWebVitals();
    
          

    As you can see above we wrapped the App component in the MsalProvider. We also created the axios interceptor so when we connect to our endpoints, the accessToken will be attached to the request. You can also notice that in order to acquire the access token, we use the msalInstance.getAllAccounts()[0] in order to get the account who is signed in.

    To learn more about MSAL.js, visit this link here.

    6. Create Sign and Signout components

    6.1 For SignIn.tsx here's our code:

            import { useMsal } from "@azure/msal-react";
    import { loginApiRequest } from "../authConfig";
    
    export const SignInButton = () => {
      const { instance } = useMsal();
    
      const handleLogin = () => {
        instance.loginRedirect(loginApiRequest).catch((e) => {
          console.log(e);
        });
      };
      return <button onClick={handleLogin}>Sign In</button>;
    };
    
          

    6.1 For Signout.tsx here's our code:

            import { useMsal } from "@azure/msal-react";
    
    export const SignOutButton = () => {
      const { instance } = useMsal();
    
      const handleLogout = () => {
        instance.logoutRedirect({
          postLogoutRedirectUri: "/",
        });
      };
      return <button onClick={handleLogout}>Sign In</button>;
    };
    
          
    7. Create Employee component that will connect to our Web API endpoints

    7.1 Paste the following codes below for reference.

            import axios from "axios";
    import { useState } from "react";
    export const Employee = () => {
      const [employees, setEmployees] = useState<string[]>([]);
    
      const getEmployees = () => {
        axios
          .get("employees/")
          .then((response) => {
            setEmployees(response.data);
          })
          .catch((error) => {
            if (error.response.status === 403) {
              alert("Your access is not allowed.");
            }
          });
      };
    
      const getTotalEmployees = () => {
        axios
          .get("employees/total-employees")
          .then((response) => {
            alert(`The total employees: ${response.data}`);
          })
          .catch((error) => {
            if (error.response.statusCode === 401) {
              alert("Unauthorized");
            }
          });
      };
    
      return (
        <div>
          <button className="button is-success" onClick={getEmployees}>
            Get Employees
          </button>
          <button className="button" onClick={getTotalEmployees}>
            Get Total Employees
          </button>
          <ol>
            {employees.map((d: string, index: number) => {
              return <li key={index}>{d}</li>;
            })}
          </ol>
        </div>
      );
    };
    
          
    8. Update App.tsx

    Here, we put everything inside our App.tsx.

            import {
      AuthenticatedTemplate,
      UnauthenticatedTemplate,
    } from "@azure/msal-react";
    import { Employee } from "./components/Employee";
    import { SignInButton } from "./components/SignInButton";
    import { SignOutButton } from "./components/SignOutButton";
    
    function App() {
      return (
        <div>
          <section className="section">
            <div className="container">
              <div className="columns is-centered">
                <div className="column is-half">
                  <AuthenticatedTemplate>
                    <SignOutButton />
                    <Employee />
                  </AuthenticatedTemplate>
                  <UnauthenticatedTemplate>
                    <SignInButton />
                    <h4 className="title is-4">
                      Please sign in to view employee info.
                    </h4>
                  </UnauthenticatedTemplate>
                </div>
              </div>
            </div>
          </section>
        </div>
      );
    }
    
    export default App;
    
          

    As you can see above, we add the AuthenticatedTemplate as well as the UnauthenticatedTemplate. We also import our Sign In and Sign Out buttons and the Employee component.

    9. Verify the api.scope if it's properly inherited in the app registration of our React.js application.

    9.1 Go to the Azure AD app registration of the react.js and choose the API permissions on the left menu. Notice the Other permissions granted for Default Directory the web api is already listed there.

    10. Run and test the functionality of the application.

    Here, I prepare a gif file that shows the demo of the application. As you can remember we set role for a specific user which is for Samp White. Samp White has the admin access so this user can either view the employee records or check the total # of employees. While the user Mark Vincent has only the access to view the total employee.

    Summary

    On this final part of the series, we work on setting up the Azure AD for our frontend application, configure our React.js to use our app registrations and connect to our ASP.NET Core web api. As the result, we're able to authenticate and authorize certain users using Azure AD to access the endpoints in our web api.

    If you have some questions or comments, please drop it below 👇 :)

    Buy Me A Tea