I have a react application and aspcore backend running on the same domain. (everything is redirected to /index.html except /api/
My users need to login Azure AD.
const msalConfig = {
auth: {
clientId: "{clientId}",
authority: "https://login.microsoftonline.com/{tenantId}",
redirectUri: `${window.location.origin}`,
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: false
}
};
const root = ReactDOM.createRoot(
document.getElementById('root') as HTMLElement
);
const msalInstance = new PublicClientApplication(msalConfig);
root.render(
<StrictMode><MsalProvider instance={msalInstance}>
<App />
</MsalProvider>
</StrictMode>
);
Everytime I call the backend I get the token by:
// Try to acquire token silently
const tokenResult = await this.msalInstance.acquireTokenSilent({
account: this.account,
scopes: [{clientId}],
});
return tokenResult.accessToken;
Now I need to verify the token in my aspcore backend.
I tried serveral ways of adding Authentication. I had hoped this would be suffient:
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
appsettings:
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "{tenantId}",
"ClientId": "{clientId}",
"Issuer": "https://login.microsoftonline.com/{tenantId}/v2.0/",
"Audience": "{clientId}"
},
I still get a 401 status code. I've registered one app in the Microsoft Entra Admin Center. Didn't think I had to create two seperate registration because my site runs on the same domain. But I might be wrong. Can someone help me with the correct configuration?
I created a sample React app frontend and an ASP. NET Core backend with Azure AD integration.
401 status code.
I got above error because of using wrong "Audience":"{ClientID}"
,so I changed the
"Audience": "api://{ClientId}"
.
appsettings.json:
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "{TenantId}",
"ClientId": "{ClientId}",
"Audience": "api://{ClientId}",
"Issuer": "https://login.microsoftonline.com/{TenantId}/v2.0"
}
Below is my complete program.cs class.
Program.cs:
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Identity.Web;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApi(builder.Configuration.GetSection("AzureAd"));
builder.Services.AddControllers();
builder.Services.AddCors(options =>
{
options.AddPolicy("AllowReactApp", policy =>
{
policy.WithOrigins("http://localhost:3000")
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials();
});
});
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
var app = builder.Build();
app.UseCors("AllowReactApp");
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
DataController.cs:
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
namespace serverapi.Controllers
{
[Authorize]
[ApiController]
[Route("api/[controller]")]
public class DataController : ControllerBase
{
[HttpGet]
public IActionResult Get()
{
return Ok(new { message = "Hello from API!" });
}
}
}
Below is my complete React Application, I used scope as
scopes: ['api://{ClientId}/access_as_user']
in react app.
App.js:
import React, { useEffect, useState } from 'react';
import { useMsal } from '@azure/msal-react';
function App() {
const { instance, accounts } = useMsal();
const [apiResponse, setApiResponse] = useState('');
const login = () => {
instance.loginPopup({
scopes: ['api://{ClientId}/access_as_user'],
});
};
const callApi = async () => {
try {
const tokenResponse = await instance.acquireTokenSilent({
account: accounts[0],
scopes: ['api://{ClientId}/access_as_user'],
});
const accessToken = tokenResponse.accessToken;
const response = await fetch('https://localhost:7165/api/Data', {
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
const data = await response.json();
setApiResponse(data);
} catch (error) {
console.error('API call error: ', error);
}
};
return (
<div>
<h1>React + Azure AD</h1>
{!accounts.length && <button onClick={login}>Login</button>}
{accounts.length && <button onClick={callApi}>Call API</button>}
<pre>{apiResponse && JSON.stringify(apiResponse, null, 2)}</pre>
</div>
);
}
export default App;
index.js:
import React from 'react';
import ReactDOM from 'react-dom/client';
import { PublicClientApplication } from '@azure/msal-browser';
import { MsalProvider } from '@azure/msal-react';
import App from './App';
const msalConfig = {
auth: {
clientId: '{Client ID}',
authority: 'https://login.microsoftonline.com/{TenantId}',
redirectUri: `${window.location.origin}`,
},
cache: {
cacheLocation: 'sessionStorage',
storeAuthStateInCookie: false,
},
};
const msalInstance = new PublicClientApplication(msalConfig);
const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
<MsalProvider instance={msalInstance}>
<App />
</MsalProvider>
);
I added the Scope to my app registration as shown below.
Asp. net Output:
React Output: