React应用通过MSAL对接ASP.NET Core API的Azure AD认证咨询
Hey there! Let's break down your problem step by step since you're new to React and MSAL—no worries, we'll cover all the bases to get your React frontend authenticated with Azure AD and talking to your MVC API, while keeping your existing database role management intact.
First, let's clarify the end-to-end flow so you know what's happening:
- User visits your React app and clicks "Login with Azure AD".
- React uses MSAL.js to trigger an Azure AD login popup/redirect.
- User logs in, Azure AD issues an Access Token for your MVC API.
- React receives and securely stores the token.
- When React calls your MVC API, it sends the token in the
Authorization: Bearer <token>header. - MVC validates the Azure AD token to confirm the user's identity, then fetches the user's roles from your Azure DB to enforce permissions.
React端: Configure MSAL & Handle Token Passing
First, set up MSAL in your React app to handle login and token acquisition.
1. Install MSAL Packages
Run this in your React project:
npm install @azure/msal-react @azure/msal-browser
2. Configure MSAL Instance
Create a config file to hold your Azure AD app details:
// src/authConfig.js import { PublicClientApplication } from '@azure/msal-browser'; // Replace these values with your Azure AD app registration details export const msalConfig = { auth: { clientId: 'YOUR_REACT_APP_CLIENT_ID', // From your SPA app registration authority: 'https://login.microsoftonline.com/YOUR_TENANT_ID', // Your Azure AD tenant ID redirectUri: 'http://localhost:3000', // Must match the redirect URI in your SPA app registration }, cache: { cacheLocation: 'sessionStorage', // Securely store tokens storeAuthStateInCookie: false, } }; // Configure your MVC API's scope (from your Web API app registration) export const apiConfig = { scopes: ['api://YOUR_MVC_API_CLIENT_ID/access_as_user'], // Replace with your API's scope uri: 'http://localhost:5000/api/TestAPI' // Your MVC API endpoint }; export const msalInstance = new PublicClientApplication(msalConfig);
3. Add Login Component
Create a simple login button to trigger Azure AD login:
// src/components/LoginButton.jsx import { useMsal } from '@azure/msal-react'; import { apiConfig } from '../authConfig'; export const LoginButton = () => { const { instance } = useMsal(); const handleLogin = async () => { try { // Trigger popup login and request API access scope await instance.loginPopup({ scopes: apiConfig.scopes, prompt: 'select_account' // Force account selection (optional) }); } catch (error) { console.error('Login failed:', error); } }; return <button onClick={handleLogin}>Login with Azure AD</button>; };
4. Call MVC API with Token
Create a helper function to fetch data from your API while attaching the token:
// src/api/apiClient.js import { msalInstance, apiConfig } from '../authConfig'; export async function callProtectedApi() { // Get the currently logged-in account const activeAccount = msalInstance.getActiveAccount(); if (!activeAccount) { throw new Error('No user logged in! Please login first.'); } // Get a silent access token for the API const tokenResponse = await msalInstance.acquireTokenSilent({ account: activeAccount, scopes: apiConfig.scopes }); // Call the API with the Bearer token in headers const response = await fetch(apiConfig.uri, { method: 'GET', headers: { 'Authorization': `Bearer ${tokenResponse.accessToken}`, 'Content-Type': 'application/json' } }); if (!response.ok) { throw new Error(`API request failed: ${response.statusText}`); } return await response.json(); }
MVC后端: Configure Authentication & API Validation
Now update your MVC app to accept and validate Azure AD tokens, while still using your existing database for roles.
1. Update Startup.cs
First, install the Microsoft Identity Web NuGet package:
Install-Package Microsoft.Identity.Web
Then modify ConfigureServices and Configure:
// Startup.cs using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Identity.Web; using Microsoft.EntityFrameworkCore; public class Startup { public IConfiguration Configuration { get; } public Startup(IConfiguration configuration) { Configuration = configuration; } public void ConfigureServices(IServiceCollection services) { // Keep your existing DB context to access users/roles services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); // Configure Azure AD authentication for API services.AddMicrosoftIdentityWebApiAuthentication(Configuration) .EnableTokenAcquisitionToCallDownstreamApi() .AddInMemoryTokenCaches(); // Enable CORS to allow requests from your React app services.AddCors(options => { options.AddPolicy("ReactCorsPolicy", policy => { policy.WithOrigins("http://localhost:3000") // Your React app URL .AllowAnyHeader() .AllowAnyMethod(); }); }); services.AddControllers(); // If you still need MVC views, keep this: services.AddControllersWithViews(); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseRouting(); // Apply CORS policy BEFORE authentication/authorization app.UseCors("ReactCorsPolicy"); app.UseAuthentication(); // Validate incoming tokens app.UseAuthorization(); // Enforce permissions app.UseEndpoints(endpoints => { endpoints.MapControllers(); // Keep this if you need MVC views: // endpoints.MapControllerRoute( // name: "default", // pattern: "{controller=Home}/{action=Index}/{id?}"); }); } }
2. Update appsettings.json
Add Azure AD configuration to your appsettings.json:
"AzureAd": { "Instance": "https://login.microsoftonline.com/", "Domain": "your-tenant-domain.onmicrosoft.com", "TenantId": "YOUR_TENANT_ID", "ClientId": "YOUR_MVC_API_CLIENT_ID", // From your Web API app registration "Audience": "api://YOUR_MVC_API_CLIENT_ID" // Must match your API's audience }
3. Modify TestAPIController
Update your controller to validate the token, fetch the user's roles from your DB, and enforce permissions:
// Controllers/TestAPIController.cs using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using Microsoft.Identity.Web; using System.Linq; [Authorize] // Ensures only valid Azure AD tokens are accepted [ApiController] [Route("api/[controller]")] public class TestAPIController : ControllerBase { private readonly ApplicationDbContext _dbContext; public TestAPIController(ApplicationDbContext dbContext) { _dbContext = dbContext; } [HttpGet] public IActionResult GetProtectedData() { // Get the user's UPN (unique identifier from Azure AD token) var userUpn = User.GetUpn(); // Alternative: Use email if that's what's stored in your DB // var userEmail = User.FindFirst(System.Security.Claims.ClaimTypes.Email)?.Value; // Fetch user from your local database var localUser = _dbContext.Users.FirstOrDefault(u => u.Email == userUpn); if (localUser == null) { return Unauthorized("User not found in local database."); } // Fetch user's roles from your DB var userRoles = _dbContext.UserRoles .Where(ur => ur.UserId == localUser.Id) .Join(_dbContext.Roles, ur => ur.RoleId, r => r.Id, (ur, r) => r.Name) .ToList(); // Example: Enforce role-based access if (!userRoles.Contains("Admin")) { return Forbid("You do not have permission to access this resource."); } return Ok(new { Message = "Success! You accessed the protected API.", UserUPN = userUpn, LocalRoles = userRoles }); } }
Key Details You Need to Know
Let's cover some critical points to avoid pitfalls:
Azure AD App Registrations:
- You need two separate app registrations: one for your React SPA, one for your MVC Web API.
- For the SPA app: Add your React redirect URI (e.g.,
http://localhost:3000) under Authentication > SPA and enable ID/access tokens. - For the Web API app: Create a scope (e.g.,
access_as_user) under Expose an API, then go to your SPA app's API Permissions to add this scope and grant admin consent.
Token Types:
- Always use an Access Token when calling APIs (not an ID Token). ID Tokens are for authenticating users in the frontend, while Access Tokens are for authorizing API access.
Role Management:
- Since your roles are stored in your own DB, Azure AD won't include them in the token. Your MVC backend must map the Azure AD user (via UPN/email/OID) to your local user record and fetch roles from there.
Token Validation:
- The
AddMicrosoftIdentityWebApiAuthenticationmethod automatically validates the token's signature, expiration date, audience, and issuer—you don't need to write custom validation logic.
- The
CORS:
- Don't forget to configure CORS in your MVC app to allow requests from your React domain. Without this, you'll get browser-side CORS errors.
内容的提问来源于stack exchange,提问作者Sebastian Siemens

