DEV Community

markzzw
markzzw

Posted on

The practice of using Microsoft OAuth in the React hook

OAuth is helpful to let developers get user information easily, here I will show you the usage with the Microsoft OAuth in the react hook way, let's start the travel.

Config in Azure

Register your app

register-application
record-identifiers

Note: Remember the application ID and Directory ID we will use them in the next step

Set up your react project

Node modules

First, we need to install the dependencies.

npm install @azure/msal-browser
npm install @azure/msal-react
Enter fullscreen mode Exit fullscreen mode

Create a msconfig.js file

msconfig.js

export const msalConfig = {
    auth: {
        clientId: "Enter_the_Application_Id_Here", // application id
        authority: "https://login.microsoftonline.com/Enter_the_Tenant_Info_Here", // directory id
        redirectUri: "http://localhost:3000", // after auth success, will redirect in this uri
    },
    cache: {
        cacheLocation: "sessionStorage", // This configures where your cache will be stored
    },
}
Enter fullscreen mode Exit fullscreen mode

Create a Microsoft auth provider

If we want to use it in the react hook way, we need to create a provider.

MsAuthProvider.jsx

import * from "react";
import { MsalProvider } from '@azure/msal-react';
import { PublicClientApplication } from '@azure/msal-browser';
import { msalConfig } from './msconfig.js';

// create msal instance and export it for use.
export const msalInstance = new PublicClientApplication(msalConfig);

export default function MsAuthProvider({ children }) {
    return <MsalProvider instance={msalInstance}>
        {children}
    </MsalProvider>
}
Enter fullscreen mode Exit fullscreen mode

Use MsAuthProvider to warp your react application

In the App.jsx, we need to do the refactor as the follows.

import * from 'react';
import { AuthenticatedTemplate, UnauthenticatedTemplate } from '@azure/msal-react';
import App from './App.jsx'
import MsAuthProvider from './msAuthProvider/index.jsx';
import SignIn from './pages/signin/index.jsx';

ReactDOM.createRoot(document.getElementById('root')!).render(
    <MsAuthProvider>
        <AuthenticatedTemplate>
          <App />
        </AuthenticatedTemplate>
        <UnauthenticatedTemplate>
          <SignIn />
        </UnauthenticatedTemplate>
    </MsAuthProvider>
)
Enter fullscreen mode Exit fullscreen mode

We can use the AuthenticatedTemplate and UnauthenticatedTemplate provided by the @azure/msal-react, it's helpful to control of the displaying component, we don't need to handle the authrized status, MsAuthProvider will handle this for the app.

Create SignIn component

SignIn component index.jsx

import * from 'react';
import { Card, Typography, CardBody, CardFooter, Button } from "@material-tailwind/react";
import { useSelector } from "react-redux";
import { useMsal } from "@azure/msal-react";

export default function SignIn() {
    const handleLogin = () => {
        // this will open a popup for signin, after singin popup will be close, then
        // app will redirect to the redirectUri as the config in msalConfig
        instance.loginPopup({
            scopes: ["User.Read"]
        }).catch((e) => console.log(e));
    };

    return (
        <div className="flex justify-center mt-32">
            <Card className="w-96">
                <CardBody className="flex flex-col gap-4 place-items-center">
                    <Typography variant="h3">
                        Sign In
                    </Typography>
                </CardBody>
                <CardFooter className="pt-0">
                    <Button
                        className="bg-blue-900"
                        fullWidth
                        onClick={handleLogin}
                    >
                        Sign In With Microsoft
                    </Button>
                </CardFooter>
            </Card>
        </div>
    )
}
Enter fullscreen mode Exit fullscreen mode

pick-account
enter-code.

Get user information

profile.jsx

import *, { useEffect } from "react";
import { useMsal } from '@azure/msal-react';

export default function Profile() {
    const [profileData, setProfileData] = useState(null);
    const { instance, accounts } = useMsal();
    const getProfile = async () => {
        // get access token by instance, this instance is provided by MsAuthProvider
        const accessToken = await instance.acquireTokenSilent({
            scopes: ["User.Read"]
            account: accounts[0],
        }).then((response) => response.accessToken);
        const headers = new Headers();
        const bearer = `Bearer ${accessToken}`;
        headers.append("Authorization", bearer);
        const options = {
            method: "GET",
            headers: headers
        };
        // fetch user information
        fetch("https://graph.microsoft.com/v1.0/me", options)
            .then(res => res.json())
            .then((res) => setProfileData(res))
    }

    useEffect(() => {
        if (!profileData) {
            getProfile();
        }
    }, [profileData]);

    return (
        <div className="flex flex-col items-center">
            {
                profileData && <div className="flex flex-col items-center">
                    <p>
                        <strong>First Name: </strong> {profileData.givenName}
                    </p>
                    <p>
                        <strong>Last Name: </strong> {profileData.surname}
                    </p>
                    <p>
                        <strong>Email: </strong> {profileData.userPrincipalName}
                    </p>
                    <p>
                        <strong>Id: </strong> {profileData.id}
                    </p>
                </div>
            }
            <button onClick={getProfile}>Get user information</button>
        </div>
    );
};
Enter fullscreen mode Exit fullscreen mode

Here we need to get access token so we use the ["User.Read"] as the scpoes param, if you want to get id token, you should use the ['openid'] as the scpoes param.

Integration with axios

Axios has interceptors can help us to add some configuration before request, let's integrate axios for the requests.

request.js

import axios from 'axios'
import { msalInstance }  from './msAuthProvider/index.jsx';
const request = axios.create({
  baseURL: '/'
})
// add 
request.interceptors.request.use(async config => {
    // msalInstance is exported by msAuthProvider file, because we can not use the hook here
    const accounts = msalInstance.getAllAccounts();
    if (accounts.length > 0) {
        const accessToken = await instance.acquireTokenSilent({
            scopes: ["User.Read"]
            account: accounts[0],
        }).then((response) => response.accessToken);
        if (accessToken) {
            config.headers.Authorization = `Bearer ${accessToken}`
        }
    }
    return config;
}, function (error) {
    return Promise.reject(error);
});

export default request;
Enter fullscreen mode Exit fullscreen mode

Then we can use axios request like this.

Refactor profile.jsx

import *, { useEffect } from "react";
import { useMsal } from '@azure/msal-react';
import request from './request.js'

export default function Profile() {
    const [profileData, setProfileData] = useState(null);
    const { instance, accounts } = useMsal();
    const getProfile = () => {
        // replce fetch by axios request
        request.get("https://graph.microsoft.com/v1.0/me")
            .then((res) => setProfileData(res))
    }

    useEffect(() => {
        if (!profileData) {
            getProfile();
        }
    }, [profileData]);

    return (
        <div className="flex flex-col items-center">
            {
                profileData && <div className="flex flex-col items-center">
                    <p>
                        <strong>First Name: </strong> {profileData.givenName}
                    </p>
                    <p>
                        <strong>Last Name: </strong> {profileData.surname}
                    </p>
                    <p>
                        <strong>Email: </strong> {profileData.userPrincipalName}
                    </p>
                    <p>
                        <strong>Id: </strong> {profileData.id}
                    </p>
                </div>
            }
            <button onClick={getProfile}>Get user information</button>
        </div>
    );
};
Enter fullscreen mode Exit fullscreen mode

Reference

  1. Migration Guide from msal v1 to @azure/msal-react and @azure/msal-browser
  2. Tutorial: Register a Single-page application with the Microsoft identity platform
  3. Axios Interceptor

Top comments (0)