page_type | services | client | service | level | languages | products | platform | endpoint | urlFragment | name | description | |||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
sample |
ms-identity |
React SPA |
Azure REST Api |
200 |
|
|
Javascript |
AAD v2.0 |
ms-identity-javascript-react-tutorial |
React single-page application using MSAL React to sign-in users and call Azure REST API and Azure Storage |
This sample demonstrates a React single-page application (SPA) that signs-in users with Azure AD and calls the [Azure Resource Manager API](https://docs.microsoft.com/rest/api/resources) and [Azure Storage API](https://docs.microsoft.com/rest/api/storageservices/) using the [Microsoft Authentication Library for React](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-react) (MSAL React). |
React single-page application using MSAL React to sign-in users and call Azure REST API and Azure Storage
- Overview
- Scenario
- Contents
- Prerequisites
- Setup the sample
- Explore the sample
- Troubleshooting
- About the code
- Next Steps
- Contributing
- Learn More
This sample demonstrates a React single-page application (SPA) that signs-in users with Azure AD and calls the Azure Resource Manager API and Azure Storage API using the Microsoft Authentication Library for React (MSAL React).
Here you'll learn how to sign-in, acquire a token and call a protected web API, as well as Dynamic Scopes and Incremental Consent, working with multiple resources and securing your routes and more.
ℹ️ See the community call: Deep dive on using MSAL.js to integrate React single-page applications with Azure Active Directory
- The client React SPA uses MSAL React to sign-in a user and obtain a JWT access token for Azure Resource Manager API and Azure Storage API from Azure AD.
- The access token is used to authorize the user with Azure SDK for JavaScript to call Azure Resource Manager API and Azure Storage API
File/folder | Description |
---|---|
App.jsx |
Main application logic resides here. |
authConfig.js |
Contains authentication configuration parameters. |
pages/Home.jsx |
Contains a table with ID token claims and description |
pages/Tenant.jsx |
Calls Azure Resource Manager with Azure SDK. |
pages/BlobStorage.jsx |
Calls Azure Storage API with Azure SDK. |
azureManagement.js |
Initialize an instance of SubscriptionClient and BlobServiceClient . |
- Node.js must be installed to run this sample.
- Visual Studio Code is recommended for running and editing this sample.
- VS Code Azure Tools extension is recommended for interacting with Azure through VS Code Interface.
- A modern web browser. This sample uses ES6 conventions and will not run on Internet Explorer.
- An Azure AD tenant. For more information, see: How to get an Azure AD tenant
- A user account in your Azure AD tenant. This sample will not work with a personal Microsoft account. If you're signed in to the Azure portal with a personal Microsoft account and have not created a user account in your directory before, you will need to create one before proceeding.
- An Azure Storage account To access Azure Storage you will need an active storage account.
From your shell or command line:
git clone https://github.com/Azure-Samples/ms-identity-javascript-react-tutorial.git
or download and extract the repository .zip file.
⚠️ To avoid path length limitations on Windows, we recommend cloning into a directory near the root of your drive.
cd 2-Authorization-I\2-call-arm\SPA
npm install
There is one project in this sample. To register it, you can:
-
follow the steps below for manually register your apps
-
or use PowerShell scripts that:
- automatically creates the Azure AD applications and related objects (passwords, permissions, dependencies) for you.
- modify the projects' configuration files.
Expand this section if you want to use this automation:
⚠️ If you have never used Microsoft Graph PowerShell before, we recommend you go through the App Creation Scripts Guide once to ensure that your environment is prepared correctly for this step.-
On Windows, run PowerShell as Administrator and navigate to the root of the cloned directory
-
In PowerShell run:
Set-ExecutionPolicy -ExecutionPolicy RemoteSigned -Scope Process -Force
-
Run the script to create your Azure AD application and configure the code of the sample application accordingly.
-
For interactive process -in PowerShell, run:
cd .\AppCreationScripts\ .\Configure.ps1 -TenantId "[Optional] - your tenant id" -AzureEnvironmentName "[Optional] - Azure environment, defaults to 'Global'"
Other ways of running the scripts are described in App Creation Scripts guide. The scripts also provide a guide to automated application registration, configuration and removal which can help in your CI/CD scenarios.
To manually register the apps, as a first step you'll need to:
- Sign in to the Azure portal.
- If your account is present in more than one Azure AD tenant, select your profile at the top right corner in the menu on top of the page, and then switch directory to change your portal session to the desired Azure AD tenant.
- Navigate to the Azure portal and select the Azure Active Directory service.
- Select the App Registrations blade on the left, then select New registration.
- In the Register an application page that appears, enter your application's registration information:
- In the Name section, enter a meaningful application name that will be displayed to users of the app, for example
ms-identity-react-c2s2
. - Under Supported account types, select Accounts in this organizational directory only
- Select Register to create the application.
- In the Name section, enter a meaningful application name that will be displayed to users of the app, for example
- In the Overview blade, find and note the Application (client) ID. You use this value in your app's configuration file(s) later in your code.
- In the app's registration screen, select the Authentication blade to the left.
- If you don't have a platform added, select Add a platform and select the Single-page application option.
- In the Redirect URI section enter the following redirect URIs:
http://localhost:3000/
http://localhost:3000/redirect.html
- Click Save to save your changes.
- In the Redirect URI section enter the following redirect URIs:
- Since this app signs-in users, we will now proceed to select delegated permissions, which is is required by apps signing-in users.
- In the app's registration screen, select the API permissions blade in the left to open the page where we add access to the APIs that your application needs:
- Select the Add a permission button and then:
- Ensure that the Microsoft APIs tab is selected.
- In the list of APIs, select the API
Windows Azure Service Management API
.- Since this app signs-in users, we will now proceed to select delegated permissions, which is is requested by apps when signing-in users.
- In the Delegated permissions section, select user_impersonation in the list. Use the search box if necessary.
- Since this app signs-in users, we will now proceed to select delegated permissions, which is is requested by apps when signing-in users.
- Select the Add permissions button at the bottom.
- Select the Add a permission button and then:
- Ensure that the Microsoft APIs tab is selected.
- In the list of APIs, select the API
Azure Storage
.- Since this app signs-in users, we will now proceed to select delegated permissions, which is is requested by apps when signing-in users.
- In the Delegated permissions section, select user_impersonation in the list. Use the search box if necessary.
- Since this app signs-in users, we will now proceed to select delegated permissions, which is is requested by apps when signing-in users.
- Select the Add permissions button at the bottom.
- Still on the same app registration, select the Token configuration blade to the left.
- Select Add optional claim:
- Select optional claim type, then choose ID.
- Select the optional claim acct.
Provides user's account status in tenant. If the user is a member of the tenant, the value is 0. If they're a guest, the value is 1.
- Select Add to save your changes
- Ensure that an Azure Storage Account was created. If not, please create one.
- Assign the role
Storage Blob Data Contributor
to your user or group to have read and write access to your blob storage. Please see Assign Azure roles using the Azure portal.
- Navigate to your Azure Storage Account.
- Select the Resource sharing (CORS) blade on the left. Make sure that Blob service is selected.
- In Allowed origins add the domain name
http://localhost:3000
and make sure there is no trailing slash. - In Allowed methods select all options.
- In Allowed headers add
*
. - In Exposed headers add
*
. - Select Save.
Open the project in your IDE (like Visual Studio or Visual Studio Code) to configure the code.
In the steps below, "ClientID" is the same as "Application ID" or "AppId".
- Open the
SPA\src\authConfig.js
file. - Find the key
Enter_the_Application_Id_Here
and replace the existing value with the application ID (clientId) ofms-identity-react-c2s2
app copied from the Azure portal. - Find the key
Enter_the_Tenant_Info_Here
and replace the existing value with your Azure AD tenant/directory ID. - find the key
Enter_Storage_Account_Name
and replace the existing value with your storage account name.
From your shell or command line, execute the following commands:
cd 2-Authorization-I\2-call-arm\SPA
npm start
- Open your browser and navigate to
http://localhost:3000
. - Select the Sign In button on the top right corner.
- Select the Tenants button on the navigation bar. This will make a call to the Azure Resource Manager API.
- Select the Storage button on the navigation bar. After that use the form upload a file to Azure Storage.
- Navigate back to Azure Portal and check the uploaded file under your storage account under the
ms-identity-react-c2s2
container.
ℹ️ Did the sample not work for you as expected? Then please reach out to us using the GitHub Issues page.
Were we successful in addressing your learning objective? Consider taking a moment to share your experience with us.
Expand for troubleshooting info
Use Stack Overflow to get support from the community. Ask your questions on Stack Overflow first and browse existing issues to see if someone has asked your question before. Make sure that your questions or comments are tagged with [
azure-active-directory
react
ms-identity
adal
msal
].
If you find a bug in the sample, raise the issue on GitHub Issues.
In order to access a protected resource on behalf of a signed-in user, the app needs to present a valid Access Token to that resource owner (in this case, Azure Resource Manager API and Azure Storage API). Access Token requests in MSAL are meant to be per-resource-per-scope(s). This means that an Access Token requested for resource A with scope scp1
:
- cannot be used for accessing resource A with scope
scp2
, and, - cannot be used for accessing resource B of any scope.
The intended recipient of an Access Token is represented by the aud
claim (in this case, it should be the Microsoft Azure Resource Manager API and Azure Storage API's App ID); in case the value for the aud
claim does not mach the resource APP ID URI, the token will be considered invalid by the API. Likewise, the permissions that an Access Token grants are provided in the scp
claim. See Access Token claims for more information.
When you have to access multiple resources, initiate a separate token request for each:
// "User.Read" stands as shorthand for "graph.microsoft.com/User.Read"
const graphToken = await msalInstance.acquireTokenSilent({
scopes: [ "" ]
});
const customApiToken = await msalInstance.acquireTokenSilent({
scopes: [ "api://<myCustomApiClientId>/My.Scope" ]
});
Bear in mind that you can request multiple scopes for the same resource (e.g. User.Read
, User.Write
and Calendar.Read
for MS Graph API).
const graphToken = await msalInstance.acquireTokenSilent({
scopes: [ "User.Read", "User.Write", "Calendar.Read"] // all MS Graph API scopes
});
In case you erroneously pass multiple resources in your token request, Azure AD will throw an exception, and your request will fail.
// your request will fail for both resources
const myToken = await msalInstance.acquireTokenSilent({
scopes: [ "https://management.azure.com/user_impersonation", "api://<myCustomApiClientId>/My.Scope" ]
});
MSAL.js exposes 3 APIs for acquiring a token: acquireTokenPopup()
, acquireTokenRedirect()
and acquireTokenSilent()
. MSAL React uses these APIs underneath, while offering developers higher level hooks and templates to simplify the token acquisition process:
export const Tenant = () => {
const { instance } = useMsal();
const [tenantInfo, setTenantInfo] = useState(null);
const account = instance.getActiveAccount();
const request = {
scopes: ["https://management.azure.com/user_impersonation"],
account: account,
};
const { login, result, error } = useMsalAuthentication(InteractionType.Popup, request);
useEffect(() => {
if (!!tenantInfo) {
return;
}
if (!!error) {
// in case popup is blocked, use redirect instead
if (error.errorCode === 'popup_window_error' || error.errorCode === 'empty_window_error') {
login(InteractionType.Redirect, request);
}
console.log(error);
return;
}
const fetchData = async (accessToken) => {
const client = await getSubscriptionClient(accessToken);
const resArray = [];
for await (let item of client.tenants.list()) {
resArray.push(item);
}
setTenantInfo(resArray);
};
if (result) {
fetchData(result.accessToken).catch((error) => {
console.log(error);
});
}
}, [tenantInfo, result, error, login]);
if (error) {
return <div>Error: {error.message}</div>;
}
return <>{tenantInfo ? <TenantData response={result} tenantInfo={tenantInfo} /> : null}</>;
};
ℹ️ Please see the documentation on acquiring an access token to learn more about various methods available in MSAL.js to acquire tokens. For MSAL React in particular, see the useIsAuthenticated hook to learn more about
useMsalAuthentication
hook to acquire tokens.
Azure SDK for JavaScript contains libraries for the breadth of Azure services. Management libraries are packages that you would use to provision and manage Azure resources. Client libraries are packages that you would use to consume these resources and interact with them. While the SDK has a default authentication provider that can be used in basic scenarios, it can also be extended to use with a pre-fetched access token. To do so, we will initialize the SubscriptionClient
object and the BlobServiceClient
object, which both contain a StaticTokenCredential object of a class that implements the TokenCredential
abstraction. It takes a pre-fetched access token in its constructor as an AccessToken and returns that from its implementation of getToken()
.
/**
* Returns a subscription client object with the provided token acquisition options
*/
export const getSubscriptionClient = async (accessToken) => {
const credential = new StaticTokenCredential({
token: accessToken,
expiresOnTimestamp: accessToken.exp
});
const client = new SubscriptionClient(credential);
return client;
};
/**
* Returns a blob service client object with the provided token acquisition options
*/
export const getBlobServiceClient = async (accessToken) => {
const credential = new StaticTokenCredential({
token: accessToken,
expiresOnTimestamp: accessToken.exp,
});
const client = new BlobServiceClient(`https://${storageInformation.accountName}.blob.core.windows.net`, credential);
return client;
};
StaticTokenCredential needs to implement the TokenCredential
interface, which can be done as shown below:
class StaticTokenCredential {
constructor(accessToken) {
this.accessToken = accessToken;
}
async getToken() {
return this.accessToken;
}
}
See azureManagement.js. The Subscription client can be used in your components as shown below:
useEffect(() => {
if (!!tenantInfo) {
return;
}
if (!!error) {
// in case popup is blocked, use redirect instead
if (error.errorCode === 'popup_window_error' || error.errorCode === 'empty_window_error') {
login(InteractionType.Redirect, request);
}
console.log(error);
return;
}
const fetchData = async (accessToken) => {
const client = await getSubscriptionClient();
const resArray = [];
for await (let item of client.tenants.list()) {
resArray.push(item);
}
setTenantInfo(resArray);
};
if (result) {
fetchData(result.accessToken).catch((error) => {
console.log(error);
});
}
}, [tenantInfo, result, error, login]);
The Azure BlobServiceClient
can be used in your component as shown:
const handleSubmit = async (e) => {
e.preventDefault();
if (uploadedFile) {
try {
const client = await getBlobServiceClient(result.accessToken);
const containerClient = client.getContainerClient(storageInformation.containerName);
const hasContainer = await containerExist(client, storageInformation.containerName);
if (hasContainer) {
const blockBlobClient = containerClient.getBlockBlobClient(uploadedFile.name);
blockBlobClient.uploadData(uploadedFile);
} else {
const createContainerResponse = await containerClient.create();
const blockBlobClient = containerClient.getBlockBlobClient(uploadedFile.name);
blockBlobClient.uploadData(uploadedFile);
console.log('Container was created successfully', createContainerResponse.requestId);
}
} catch (error) {
console.log(error);
}
}
};
Learn how to:
- Use MSAL React to authorize users to call a protected web API on Azure Active Directory
- Deploy your React Application to Azure Cloud and use Azure services to manage your operations
If you'd like to contribute to this sample, see CONTRIBUTING.MD.
This project has adopted the Microsoft Open Source Code of Conduct. For more information, see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.
- Microsoft identity platform (Azure Active Directory for developers)
- Overview of Microsoft Authentication Library (MSAL)
- Register an application with the Microsoft identity platform
- Configure a client application to access web APIs
- Understanding Azure AD application consent experiences
- Understand user and admin consent
- Application and service principal objects in Azure Active Directory
- Authentication Scenarios for Azure AD
- Building Zero Trust ready apps
- National Clouds
- Azure AD code samples
- Initialize client applications using MSAL.js
- Single sign-on with MSAL.js
- Handle MSAL.js exceptions and errors
- Logging in MSAL.js applications
- Pass custom state in authentication requests using MSAL.js
- Prompt behavior in MSAL.js interactive requests
- Use MSAL.js to work with Azure AD B2C