esi-react-aad-msal
v2.2.4-fix.1
Published
A react component that integrates with Azure AD (v2, MSAL).
Downloads
9
Maintainers
Readme
React AAD MSAL
A library of components to easily integrate the Microsoft Authentication Library with Azure Active Directory in your React app quickly and reliably. The library focuses on flexibility, providing functionality to login, logout, and fetch the user details while maintaining access to the underlying MSAL library for advanced use.
:exclamation: This library is not affiliated with the Identity team at Microsoft. It was developed as a tool for the Open Source community to use and contribute to as they see fit.
:memo: Table of contents
- React AAD MSAL
:tada: Features
:white_check_mark: Login/logout with AzureAD
component
:white_check_mark: Callback functions for login success, logout success, and user info changed
:white_check_mark: withAuthentication
higher order component for protecting components, routes, or the whole app
:white_check_mark: Function as Child Component pattern (FaCC) to pass authentication data and login/logout functions to children components
:white_check_mark: Redux integration for storing authentication status, user info, tokens, etc
:white_check_mark: Automatic renewal of IdTokens, and optional function to get a fresh token at any point
:white_check_mark: Easily fetch a fresh Access Token from cache (or refresh it) before calling API endpoints
:checkered_flag: Getting Started
Prerequisites
- node.js
- Register an app in AzureAD to get a
clientId
. You will need to follow additional steps to enable the app for your SPA site.
Installation
Via NPM:
npm install react-aad-msal msal --save
Creating the Provider
Before beginning it is required to configure an instance of the MsalAuthProvider
and give it three parameters:
| Parameters | Description |
| ------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| config
| Instance of a Msal.Configuration
object to configure the underlying provider. The documentation for all the options can be found in the configuration options doc |
| parameters
| Instance of the Msal.AuthenticationParameters
configuration to identify how the authentication process should function. This object includes the scopes
values. You can see possible values for scopes here |
| options
| [Optional] The options
are defined by the IMsalAuthProviderConfig interface. This contains settings that describe how the authentication provider should handle certain authentication processes. |
The MsalAuthProvider
is meant to be a singleton. There are known implications when multiple instances of MSAL are running at the same time. The recommended approach is to instantiate the MsalAuthProvider
in a separate file and import
it when needed.
// authProvider.js
import { MsalAuthProvider, LoginType } from 'esi-react-aad-msal';
const config = {
auth: {
authority: 'https://login.microsoftonline.com/common',
clientId: '<YOUR APPLICATION ID>',
redirectUri: '<OPTIONAL REDIRECT URI>'
},
cache: {
cacheLocation: "localStorage",
storeAuthStateInCookie: true
}
};
const authenticationParameters = {
scopes: [
'<property (i.e. user.read)>',
'https://<your-tenant-name>.onmicrosoft.com/<your-application-name>/<scope (i.e. demo.read)>'
]
}
const options = {
loginType: LoginType.Popup,
tokenRefreshUri: window.location.origin + '/auth.html'
}
export const authProvider = new MsalAuthProvider(config, authenticationParameters, options)
Now you can import
the authProvider
and use it in combination with one of the authentication components.
// index.js
import React from 'react';
import ReactDOM from 'react-dom';
import { AzureAD } from 'esi-react-aad-msal';
import App from './App';
import { authProvider } from './authProvider';
ReactDOM.render(
<AzureAD provider={authProvider} forceLogin={true}>
<App />
</AzureAD>,
document.getElementById('root'),
);
Configuration Options
The options that get passed to the MsalAuthProvider
are defined by the MSAL library, and are described in more detail in the configuration options documentation.
Below is the total set of configurable options that are supported currently in the config
.
// Configuration Object
export type Configuration = {
auth: AuthOptions,
cache?: CacheOptions,
system?: SystemOptions
};
// Protocol Support
export type AuthOptions = {
clientId: string;
authority?: string;
validateAuthority?: boolean;
redirectUri?: string | (() => string);
postLogoutRedirectUri?: string | (() => string);
navigateToLoginRequestUrl?: boolean;
};
// Cache Support
export type CacheOptions = {
cacheLocation?: CacheLocation;
storeAuthStateInCookie?: boolean;
};
// Library support
export type SystemOptions = {
logger?: Logger;
loadFrameTimeout?: number;
tokenRenewalOffsetSeconds?: number;
navigateFrameWait?: number;
};
For more information on MSAL config options refer to the MSAL configuration options documentation.
Authentication Parameters
When instantiating an instance of the MsalAuthProvider
the authentication parameters passed will become the default parameters used when authenticating and fetching or refreshing tokens. It is possible to change the default parameters later by executing the setAuthenticationParameters()
method on the MsalAuthProvider
.
The set of options that are supported for the Msal.AuthenticationParameters
class can be found below as they are defined in the MSAL library.
export type AuthenticationParameters = {
scopes?: Array<string>;
extraScopesToConsent?: Array<string>;
prompt?: string;
extraQueryParameters?: {[key: string]: string};
claimsRequest?: string;
authority?: string;
state?: string;
correlationId?: string;
account?: Account;
sid?: string;
loginHint?: string;
};
Options
The options
parameter defines settings related to how the authentication provider processes authentication operations.
const options = {
// The default login type is Popup
loginType: LoginType.Popup,
// A blank html page that MSAL can load in an iframe to refresh the token
// The default setting for this parameter is `window.location.origin`
tokenRefreshUri: window.location.origin + '/auth.html'
}
LoginType
is an enum with two options for Popup
or Redirect
authentication. This parameter is optional and will default to Popup
if not provided. The tokenRefreshUri
allows you to set a separate page to load only when tokens are being refreshed. When MSAL
attempts to refresh a token, it will reload the page in an iframe. This option allows you to inform MSAL
of a specific page it can load in the iframe. It is best practice to use a blank HTML file so as to prevent all your site scripts and contents from loading multiple times.
At any time after instantiating the MsalAuthProvider
the login type can be changed using the setProviderOptions()
method. You may also retrieve the current options using the getProviderOptions()
method.
:package: Authentication Components
The library provides multiple components to integrate Azure AD authentication into your application and each component has various use cases. The are also plans for additional components, documented in the project Roadmap.
AzureAD Component
The AzureAD
component is the primary method to add authentication to your application. When the component is loaded it internally uses MSAL to check the cache and determine the current authentication state. The users authentication status determines how the component will render the children
.
- If the
children
is an element, it will only be rendered when theAzureAD
detects an authenticated user. - If the
children
is a function, then it will always be executed with the following argument:{ login, // login function logout, // logout function authenticationState, // the current authentication state error, // any error that occured during the login process accountInfo, // account info of the authenticated user }
The AzureAD
component will check that the IdToken is not expired before determining that the user is authenticated. If the token has expired, it will attempt to renew it silently. If a valid token is maintained it will be sure there is an active Access Token available, otherwise it will refresh silently. If either of the tokens cannot be refreshed without user interaction, the user will be prompted to signin again.
import { AzureAD, AuthenticationState, AuthenticationState } from 'esi-react-aad-msal';
// Import the provider created in a different file
import { authProvider } from './authProvider';
// Only authenticated users can see the span, unauthenticated users will see nothing
<AzureAD provider={authProvider}>
<span>Only authenticated users can see me.</span>
</AzureAD>
// If the user is not authenticated, login will be initiated and they will see the span when done
<AzureAD provider={authProvider} forceLogin={true}>
<span>Only authenticated users can see me.</span>
</AzureAD>
// Using a function inside the component will give you control of what to show for each state
<AzureAD provider={authProvider} forceLogin={true}>
{
({login, logout, authenticationState, error, accountInfo}) => {
switch (authenticationState) {
case AuthenticationState.Authenticated:
return (
<p>
<span>Welcome, {accountInfo.account.name}!</span>
<button onClick={logout}>Logout</button>
</p>
);
case AuthenticationState.Unauthenticated:
return (
<div>
{error && <p><span>An error occured during authentication, please try again!</span></p>}
<p>
<span>Hey stranger, you look new!</span>
<button onClick={login}>Login</button>
</p>
</div>
);
case AuthenticationState.InProgress:
return (<p>Authenticating...</p>);
}
}
}
</AzureAD>
The following props are available to the AzureAD
component.
| AzureAD Props | Description |
| ------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| provider
| An MsalAuthProvider
instance containing configuration values for your Azure AD instance. See [Creating the Provider](#Creating the Provider) for details. |
| authenticatedFunction
| :warning: [Deprecated] [Optional] A user defined callback function for the AzureAD component to consume. This function receives the AzureAD components logout function
which you can use to trigger a logout. The return value will be rendered by the AzureAD component. If no return value is provided, the children
of the AzureAD component will be rendered instead. |
| unauthenticatedFunction
| :warning: [Deprecated] [Optional] A user defined callback function for the AzureAD component to consume. This function receives the AzureAD components login function
which you can then use to trigger a login. The return value will be rendered by the AzureAD component. |
| accountInfoCallback
| :warning: [Deprecated] [Optional] A user defined callback function for the AzureAD component to consume. The AzureAD component will call this function when login is complete to pass back the user info as an instance of IAccountInfo
. In addition to the tokens, the account info includes the Msal.Account
|
| reduxStore
| [Optional] You can provide a redux store which the component will dispatch actions to when changes occur. You can find the list of all actions defined in the AuthenticationActions enum. |
| forceLogin
| [Optional] A boolean that identifies whether the login process should be invoked immediately if the current user is unauthenticated. Defaults to false
. |
Higher Order Component
Sometimes it's easier to utilize a Higher Order Component (HoC) to lock down an app or page component with authentication. This can be accomplished using the withAuthentication
component which acts as a wrapper for the AzureAD
component.
import { withAuthentication } from 'esi-react-aad-msal';
// The instance of MsalAuthProvider defined in a separate file
import { authProvider } from './authProvider.js';
// The App component wrapped in the withAuthentication() HoC
export default withAuthentication(App, {
provider: authProvider,
reduxStore: store
});
The first parameter is the component that requires authentication before being mounted. The second parameter is an object containing the props to be passed into the AzureAD
component. With this approach the forceLogin
prop will default to true. This is a good way to protect routes or quickly require authentication for your entire App
in several lines.
:mortar_board: Advanced Topics
Getting Tokens for API Requests
The library components will manage authenticating the user without you needing to think about tokens. But there are scenarios where a fresh token will be needed to communicate with a service or to decode the token to examine the claims. The library exposes methods for retrieving active IdTokens and Access Tokens.
For more advanced scenarios where you need specific control over error handling when a token fails to renew you can always access the MSAL API methods and renew a token manually as described in the MSAL token renewal pattern documentation.
Refreshing Access Tokens
To get a fresh and valid Access Token to pass to an API you can call the getAccessToken()
on the MsalAuthProvider
instance. This function will asynchronously attempt to retrieve the token from the cache. If the cached token has expired it will automatically attempt to refresh it. In some scenarios the token refresh will fail and the user will be required to authenticate again before a fresh token is provided. The method will handle these scenarios automatically.
The getAccessToken()
returns an instance of the AccessTokenResponse
class. The following snippet is an example of how you might use the function before calling an API endpoint.
// authProvider.js
import { MsalAuthProvider, LoginType } from 'esi-react-aad-msal';
export const authProvider = new MsalAuthProvider(
{
/* config */
},
{
/* parameters */
},
LoginType.Popup,
);
// api.js
import { authProvider } from './authProvider';
const request = async url => {
const token = await authProvider.getAccessToken();
return fetch(url, {
method: 'GET',
headers: {
Authorization: 'Bearer ' + token.accessToken,
'Content-Type': 'application/json',
},
});
};
Renewing IdTokens
To get a fresh and valid IdToken you can call the getIdToken()
on the MsalAuthProvider
instance. This function will asynchronously attempt to retrieve the token from the cache. If the cached token has expired it will automatically attempt to renew it. In some scenarios the token renewal will fail and the user will be required to authenticate again before a new token is provided. The method will handle these scenarios automatically.
The getIdToken()
returns an instance of the IdTokenResponse
class. The following snippet is an example of how you might use the function to retrieve a valid IdToken.
// authProvider.js
import { MsalAuthProvider, LoginType } from 'esi-react-aad-msal';
export const authProvider = new MsalAuthProvider(
{
/* config */
},
{
/* parameters */
},
LoginType.Popup,
);
// consumer.js
import { authProvider } from './authProvider';
const token = await authProvider.getIdToken();
const idToken = token.idToken.rawIdToken;
Integrating with a Redux Store
The AzureAD
component optionally accepts a reduxStore
prop. On successful login and after an Access Token has been acquired, an action of type AAD_LOGIN_SUCCESS
will be dispatch to the provided store containing the token and user information returned from Active Directory. It does the same for logout events, but the action will not contain a payload.
Import your store into the file rendering the AzureAD
component and pass it as a prop:
// authProvider.js
import { MsalAuthProvider, LoginType } from 'esi-react-aad-msal';
export const authProvider = new MsalAuthProvider(
{
/* config */
},
{
/* parameters */
},
LoginType.Popup,
);
// index.js
import { authProvider } from './authProvider';
import { store } from './reduxStore.js';
// ...
ReactDOM.render(
<AzureAD provider={authProvider} reduxStore={store}>
<App />
</AzureAD>,
document.getElementById('root'),
);
Add a case to handle AAD_LOGIN_SUCCESS
and AAD_LOGOUT_SUCCESS
actions in a reducer file:
const initialState = {
aadResponse: null,
};
const sampleReducer = (state = initialState, action) => {
switch (action.type) {
case 'AAD_LOGIN_SUCCESS':
return { ...state, aadResponse: action.payload };
case 'AAD_LOGOUT_SUCCESS':
return { ...state, aadResponse: null };
default:
return state;
}
};
In addition to login and logout actions, the MsalAuthProvider
will dispatch other actions for for various state changes. The full list can be found in the actions.ts file and in the table below. An example of a fully implemented sample reducer can be found in the sample project.
| Action Type | Payload | Description |
| --------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| AAD_INITIALIZING | None | Dispatched when the MsalAuthProvider
is instantiated and begins checking the cache to determine if the user is authenticated |
| AAD_INITIALIZED | None | Signifies that the MsalAuthProvider
has successfully determined the authentication status of the user after being instantiated. It is safest not to use any state until initialization has completed |
| AAD_AUTHENTICATED_STATE_CHANGED | AuthenticationState
| Dispatched whenever the user's authentication status changes |
| AAD_ACQUIRED_ID_TOKEN_SUCCESS | IdTokenResponse
| Identifies that the IdToken has been retrieved or renewed successfully |
| AAD_ACQUIRED_ID_TOKEN_ERROR | Msal.AuthError
| Dispatched when an error occurred while attempting to retrieve or renew the IdToken |
| AAD_ACQUIRED_ACCESS_TOKEN_SUCCESS | AccessTokenResponse
| Identifies that the Access Token has been retrieved or refreshed successfully |
| AAD_ACQUIRED_ACCESS_TOKEN_ERROR | Msal.AuthError
| Dispatched when an error occurred while attempting to retrieve or refresh the Access Token |
| AAD_LOGIN_SUCCESS | IAccountInfo
| Dispatched when the user has been authenticated and a valid Access Token has been acquired |
| AAD_LOGIN_FAILED | None | Dispatched when the authentication process fails |
| AAD_LOGIN_ERROR | Msal.AuthError
| Identifies that an error occurred while login was in process |
| AAD_LOGOUT_SUCCESS | None | Dispatched when the user has successfully logged out on the client side |
Accessing the MSAL API
While this wrapper attempts to provide a full-featured means of authenticating with AzureAD, for advanced cases you may want to access the underlying MSAL API. The MsalAuthProvider
extends the MSAL UserAgentApplication
class and will give you access to all the functions available, in addition to implementing new methods used by the library components.
const authProvider = new MsalAuthProvider(config, authenticationParameters);
// authProvider provides access to MSAL features
It is not recommended to use this method if it can be avoided, since operations executed via MSAL may not reflect in the wrapper.
:cd: Sample application
If you'd like to see a sample application running please see the sample application built with Create React App.
The project can be built with the following steps:
git clone https://github.com/syncweek-react-aad/react-aad.git
cd ./react-aad
- Build the
react-aad
library:npm install
npm run build
- Run the sample project:
npm start
Be sure to modify the authProvider.js configuration to specify your own ClientID
and Authority
.
:calendar: Roadmap
While the library is ready for use there is still plenty of ongoing work. The following is a list of a few of the improvements under consideration.
:white_medium_small_square: Rewrite the sample app to use hooks and simplify the logic.
:white_medium_small_square: Add a useAuthentication()
hook to the library.
:white_medium_small_square: Replace the AzureAD
render props with event handlers.
:white_medium_small_square: Add Context API provider.
:white_medium_small_square: Separate MSAL and Redux dependencies as peerDependencies
:white_medium_small_square: Migrate to a build system such as Webpack, or Rollup.
:white_medium_small_square: Add samples for consuming a Web API.
:white_medium_small_square: Improve unit test coverage across the library.
:white_medium_small_square: Maintain feature parity between the official MSAL Angular library after it undergoes its planned upgrade.
:books: Resources
The following resources may be helpful and provide further insight. If you've written a blog post, tutorial, or article feel free to create an issue so we can include it.
:trophy: Contributers
This library is built with :heart: by members of the open source community. To become a contributer, please see the contribution guidelines.