Introduction:
This article provides a comprehensive, step-by-step guide for the API project – an ASP.NET Core 8.0 Web API that is protected by Microsoft EntraID (formerly Azure Active Directory) using the Microsoft Identity Web (MSAL) library. The API validates incoming JWT bearer tokens issued by Azure AD and enforces scope-based authorization before allowing access to protected endpoints.
Authentication Flow (Conceptual)
1) The client application (e.g., a SPA, mobile app, or Postman) requests an access token from Microsoft Entra ID by authenticating with its Client ID and Client Secret / Certificate.
2) Microsoft Entra ID validates the credentials and issues a JWT access token containing the requested scopes (e.g., “weather.read”).
3) The client sends an HTTP request to the Web API with the access token in the Authorization header as a Bearer token.
4) The ASP.NET Core middleware (Microsoft.Identity.Web) validates the JWT token – checking the signature, issuer, audience, and expiry.
5) The authorization policy (“AuthZPolicy”) verifies that the token contains the required scope (“weather.read”).
6) If validation succeeds, the API processes the request and returns the weather forecast data. Otherwise, it returns a 401 Unauthorized or 403 Forbidden response.
Pre-requistes
– .NET 8.0 + SDK installed
– Visual Studio 2022 (or VS Code with C# Dev Kit)
– An App Registration created in the Entra ID Admin portal for the Web API
– A separate App Registration for the client application (to obtain tokens)
– Postman installed for API testing
NuGet Package Manager
- Microsoft.AspNetCore.Authentication.JwtBearer
- Microsoft.Identity.Web
MS Entra ID Configuration
The appsettings.json file contains the Azure AD configuration that
Microsoft.Identity.Web uses to validate incoming JWT tokens.
File: appsettings.json
{
"AzureAd": {
"Instance": "https://login.microsoftonline.com/",
"TenantId": "<your-tenant-id>",
"ClientId": "<your-client-id>",
"Scopes": "weather.read"
},
"AllowedHosts": "*"
}
Configuration Property Explanation:
Instance -> The Azure AD authority URL base. For public Azure AD, this is always “https://login.microsoftonline.com/”.
TenantId -> The Directory (Tenant) ID of your Azure AD tenant. This identifies which tenant issued the token. The middleware validates that the token’s “tid” claim matches this value, ensuring tokens from other tenants are rejected.
ClientId -> The Application (Client) ID of the Web API app registration. This is used as the expected “audience” (aud claim) in the JWT token. Only tokens intended for this specific API will be
Scopes ->The required scope(s) that must be present in the token’s “scp” claim. In this project, “weather.read” is required. The authorization policy references this configuration key to enforce scope validation
The TenantId and ClientId values shown above are specific to this project’s Azure AD app registration. ECS team will provide those values
Authentication & Authorization Middleware
Program.cs is the application entry point where the authentication and authorization middleware is configured. This is where MSAL-based JWT token validation is set up.
Code block explanation
Adding Authentication with MSAL
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddMicrosoftIdentityWebApi(builder.Configuration.GetSection(“AzureAd”));
This is the core MSAL integration code. Here is what happens under the hood:
1) AddAuthentication(JwtBearerDefaults.AuthenticationScheme) registers the authentication services and sets “Bearer” as the default scheme. This means all [Authorize] attributes will expect a JWT Bearer token by default.
2) AddMicrosoftIdentityWebApi() reads the “AzureAd” configuration section and configures the JWT Bearer handler with the following validation
parameters:
– Authority: Constructed from Instance + TenantId
(e.g., “https://login.microsoftonline.com/0496c54d-…/v2.0”)
– Audience: Set to the ClientId value (“7627c0e7-…”)
– Issuer: Validated against the Azure AD metadata endpoint
– Signing Keys: Automatically downloaded from the Azure AD JWKS endpoint
(https://login.microsoftonline.com/{tenantId}/discovery/v2.0/keys)
– Token Lifetime: Validated to ensure the token has not expired
– Token Type: Must be an access token (not an ID token)
Adding Authorization with Scope Policy
builder.Services.AddAuthorization(config =>
{
config.AddPolicy("AuthZPolicy", policyBuilder =>
policyBuilder.Requirements.Add(
new ScopeAuthorizationRequirement()
{
RequiredScopesConfigurationKey = "AzureAd:Scopes"
}));
});
This code creates a named authorization policy called “AuthZPolicy” that:
1) Creates a ScopeAuthorizationRequirement – a class from Microsoft.Identity.Web that checks the “scp” (scope) claim in the JWT token.
2) The RequiredScopesConfigurationKey property points to “AzureAd:Scopes” in appsettings.json, which resolves to “weather.read”.
3) When a request hits a controller decorated with
[Authorize(Policy = “AuthZPolicy”)], the middleware checks that the JWT
token’s “scp” claim contains “weather.read”. If the scope is missing, a 403 Forbidden response is returned
Middleware Pipeline Order (CRITICAL)
- app.UseAuthentication();
- app.UseHttpsRedirection();
- app.UseAuthorization();
- app.MapControllers();
The order of middleware is critical:
1) UseAuthentication() must come BEFORE UseAuthorization(). It reads the
Bearer token from the Authorization header, validates it using the MSAL-configured handler, and populates HttpContext.User with the claims principal.
2) UseHttpsRedirection() redirects HTTP requests to HTTPS for security.
3) UseAuthorization() evaluates the authorization policies (including “AuthZPolicy”) against the authenticated user’s claims.
4) MapControllers() maps the controller endpoints to the routing pipeline.
Protecting the API Controller
The WeatherForecastController is the protected API endpoint. It uses the
[Authorize] attribute with the “AuthZPolicy” policy to enforce both
authentication and scope-based authorization.
Code block explanation
[Authorize(Policy = “AuthZPolicy”)]
This attribute is applied at the controller level, meaning every action in this controller requires:
1) A valid JWT Bearer token in the Authorization header (authentication).
2) The token must contain the “weather.read” scope (authorization via AuthZPolicy).
If no token is provided –> API returns 401 Unauthorized
If the token lacks the scope –> API returns 403 Forbidden
How JWT Token Validation Works
When a client sends a request to the protected API, the following validation steps occur in sequence:
Step 1: Token Extraction
The JwtBearer middleware extracts the token from the HTTP Authorization header. The header must be in the format:
Authorization: Bearer <token>
If the header is missing or malformed, a 401 Unauthorized response is returned immediately.
Step 2: Token Parsing & Signature Validation
The middleware parses the JWT token (which is a Base64URL-encoded string with three parts: Header, Payload, and Signature). It then downloads the Azure AD public signing keys from the JWKS (JSON Web Key Set) endpoint and verifies the token’s digital signature using RSA. This ensures the token was actually issued by Microsoft Entra ID and has not been tampered with.
Step 3: Issuer Validation
The middleware validates the “iss” (issuer) claim in the token. It must match the expected issuer URL:
https://login.microsoftonline.com/{TenantId}/v2.0
This prevents tokens issued by other identity providers from being accepted.
Step 4: Audience Validation
The middleware validates the “aud” (audience) claim. It must match the ClientId configured in appsettings.json (7627c0e7-8633-49a2-a92a-cd6912aa7b2b). This ensures the token was intended for this specific API and not some other application.
Step 5: Token Lifetime Validation
The middleware checks the “exp” (expiration) and “nbf” (not before) claims to ensure the token is currently valid and has not expired. Azure AD tokens Typically have a lifetime of 60-90 minutes.
Step 6: Scope Authorization
After the token passes all validation checks, the authorization middleware evaluates the “AuthZPolicy” policy. The ScopeAuthorizationRequirement checks that the “scp” claim in the token contains “weather.read”. If the scope is not present, a 403 Forbidden response is returned.
Step 7: Request Processing
Only after ALL validation steps pass does the request reach the controller’s action. The authenticated user’s claims are available via HttpContext.User throughout the request pipeline.
Testing the API with POSTMAN
This section explains how to obtain a JWT access token from Azure AD and use it to call the protected WeatherForecast API using Postman.
Obtain an Access Token
Using Postman’s Built-in OAuth 2.0 (Recommended)
1) Open Postman and create a new HTTP request.
2) Go to the “Authorization” tab.
3) Set the “Auth Type” to “OAuth 2.0”.
4) Click “Configure New Token” and fill in the following details:
- Token Name EntraID-Token (or any descriptive name)
- Grant Type Authorization Code (with PKCE) or Client Credentials
- Callback URL Turn on Authorize using browser.
Add the callback URL as a redirect URI of your client app registered in MS Entra ID App registration
- Auth URL https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/authorize
- Access Token URL https://login.microsoftonline.com/{tenant-id}/oauth2/v2.0/token
- Client ID <Your Client App’s Client ID>
- Client Secret <Your Client App’s Client Secret>
- Scope api://{appid}/weather.read
Click “Get New Access Token”. Postman will redirect you to the Microsoft login page (for Authorization Code flow)
After authentication, the access token will appear in Postman. Click “Use Token”.
Call the Protected API
1) Create a new GET request in Postman.
2) Set the URL to:
https://localhost:7100/WeatherForecast
3) Go to the “Authorization” tab:
– If you used Option A (OAuth 2.0), the token is already attached.
Make sure “Bearer Token” is selected and the token is populated.
– If you used Option B (manual), select “Bearer Token” as the auth
type and paste the access_token value you copied.
4) Alternatively, you can set the header manually:
Key: Authorization
Value: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIs…
5) Click “Send”.
Summary:
This article explains how to secure a .NET Web API using Microsoft Entra ID by implementing JSON Web Token validation. It walks through the complete setup, including prerequisites, project structure, required NuGet packages, and Entra ID configuration in appsettings.json. The document demonstrates how to configure authentication and authorization middleware in the application, protect API controllers, and understand the end-to-end JWT token validation flow. It also shows how to obtain an access token using the Microsoft Authentication Library (MSAL) and test secured API endpoints in Postman, helping developers verify that only authenticated requests can access protected resources.
Check my repo