Introduction

In enterprise environments, securing APIs while maintaining developer productivity is a constant balancing act. Traditional approaches often involve:

These practices introduce security vulnerabilities and operational complexity that modern cloud-native applications cannot afford to have.

This guide demonstrates how to build a Blazor Server application that securely calls APIs hosted behind Azure API Management (APIM) using a passwordless authentication approach. Developers authenticate using their own identities through Visual Studio or Azure CLI, while production workloads use Managed Identities—all through a unified DefaultAzureCredential implementation.

Security Risks of Sharing Secrets

  1. Secret Sprawl: Client secrets scattered across developer machines, configuration files, and deployment pipelines

  2. Operational Overhead: Regular secret rotation, secure distribution, and lifecycle management

  3. Security Risks: Accidental exposure through version control, logs, or compromised developer workstations

  4. Audit Challenges: Difficulty tracking which developer or system made specific API calls

  5. Compliance Issues: Inability to enforce the principle of least privilege at the individual developer level

This article presents a comprehensive solution that eliminates these challenges by leveraging Microsoft Entra ID (formerly Azure Active Directory), DefaultAzureCredential, and Azure API Management, all while adhering to Zero Trust principles. Our implementation enables developers to authenticate using their own identities while maintaining strict security controls through app roles and APIM policies.

Traditional Secret-Based Authentication

In typical enterprise scenarios, development teams access backend APIs protected by Azure API Management. The traditional approach involves:

This approach creates several problems:

Security Concerns

Operational Burden

Compliance Challenges

Understanding DefaultCredential

DefaultAzureCredential is a credential chain provided by the Azure Identity library that simplifies authentication for applications running in various environments. It attempts to authenticate using multiple credential types in the following order:

For Local Development:

  1. Environment Credential: Reads AZURE_TENANT_ID, AZURE_CLIENT_ID, and AZURE_CLIENT_SECRET

  2. Workload Identity Credential: For Kubernetes environments

  3. Managed Identity Credential: For Azure resources with managed identities

  4. Visual Studio Credential: Uses credentials from Visual Studio

  5. Visual Studio Code Credential: Uses credentials from the VS Code Azure extension

  6. Azure CLI Credential: Uses credentials from Azure CLI (az login)

  7. Azure PowerShell Credential: Uses credentials from Azure PowerShell

  8. Interactive Browser Credential: Opens a browser for user authentication

For Production (Azure Resources):

Managed Identity Credential: Automatically uses the identity assigned to the Azure resource

Key Benefits:

  1. Environment Flexibility: Same code works in development, testing, and production

  2. No Secret Management: Developers never handle client secrets

  3. Automatic Failover: If one credential type fails, it tries the next

  4. Security by Default: Prioritizes more secure credential types

  5. Simplified Code: Single credential object works everywhere

How does it work?

In Development:

When a developer runs the application, DefaultAzureCredential checks if they are signed in to Visual Studio Code or Azure CLI. If authenticated, it uses their personal Entra ID to acquire tokens. The token includes their user information and any app roles assigned to them through group membership.

In Production:

When deployed to Azure (App Service, Container Apps, AKS, etc.), the application automatically uses the Managed Identity assigned to the resource. No configuration changes needed. The managed identity is also assigned the necessary app roles.

The Architecture Overview

The solution consists of several components working together:

1. Microsoft Entra ID (Azure AD):

2. Dev-Team Security Group:

3. Backend API App Registration:

4. Client App Registration:

5. Azure API Management:

6. Blazor Client Application:

Step-By-Step Implementation

Step 1: Creating the App Role

App roles are application-specific permissions that can be assigned to users or groups. They appear in the token's roles claim, which APIM can validate.

Creating the App Role:

1. Navigate to Azure Portal > Entra ID > App Registrations

2. Select the backend API app registration (APIM-Weather-API)

3. Go to App roles > Create app role

4. Configure the role:

5. Save the app role

The app role is now defined but not yet assigned to anyone. This is intentional - we'll assign it to the Dev-Team group so all members automatically inherit it.

Weather APP
  1. Click on Expose an API, add a scope access_as_user

Scope
  1. Add a client application, app id : 04f0c124-f2bc-4f59-8241-bf6df9866bbd. This is a unique app ID registered in the Microsoft Entra ID for the Visual Studio

Client App

Step 2: Creating the Dev-Team Group:

1. Navigate to Azure Portal > Entra ID > Groups

2. Click New group

3. Configure:

4. Add members: Select all developers who need API access

5. Create the group

Step 3: Assigning App Role to Dev-Team Group

Now we connect the app role to the security group:

  1. Navigate to Azure Portal > Entra ID > Enterprise Registrations

  2. Select the backend API app registration (APIM-Weather-API)

  3. Click on users or group and assign the app role to the Group

What this does:

Enterprise App

Step 4: Configuring APIM Policy

Azure API Management policies allow you to modify API behavior and enforce security rules. We'll create a policy that validates JWT tokens and checks for the required app role.

The APIM Policy:

<policies>
    <inbound>
        <base />
        <validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Valid Azure AD token with Weather.Read app role required.">
            <openid-config url="https://login.microsoftonline.com/{your-tenant-id}/v2.0/.well-known/openid-configuration" />
            <audiences>
                <audience>api://{your-api-appid}</audience>
            </audiences>
            <required-claims>
                <claim name="roles" match="any">
                    <value>weather.read</value>
                </claim>
            </required-claims>
        </validate-jwt>
    </inbound>
</policies>

Why use APIM policies instead of backend validation?

Step 5: Implementing DefaultAzureCredential in the Application

Now we implement the client application that uses DefaultAzureCredential.

Application Configuration (Program.cs):

using Azure.Core;
using Azure.Identity;
using EntraID_Blazor_APIM_Client.Components;
using EntraID_Blazor_APIM_Client.Services;

var builder = WebApplication.CreateBuilder(args);

TokenCredential credential;
string authMethod;

var tenantId = {your-tenant-id};


try
{
    credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions
    {
        TenantId = tenantId,
        ExcludeEnvironmentCredential = true,  // Exclude client secret from env vars
        ExcludeWorkloadIdentityCredential = true,
        ExcludeManagedIdentityCredential = true, 
        ExcludeVisualStudioCredential = false,  // Try Visual Studio first
        ExcludeVisualStudioCodeCredential = true,  
        ExcludeAzureCliCredential = false,  // Enable Azure CLI as fallback
        ExcludeAzurePowerShellCredential = true,  
        ExcludeInteractiveBrowserCredential = true 
    });
    
    authMethod = "DefaultAzureCredential (Developer Identity)";
    
}
catch (Exception ex)
{
    Console.WriteLine($"DefaultAzureCredential setup failed: {ex.Message}");
    throw;
}

builder.Services.AddSingleton<TokenCredential>(credential);
builder.Services.AddSingleton(new AuthenticationInfo { Method = authMethod });

// Register Auth Service for dynamic authentication
builder.Services.AddSingleton(sp => 
{
    var config = sp.GetRequiredService<IConfiguration>();
    return new AuthService(credential, config, authMethod);
});

// Register HttpClient for calling the API
builder.Services.AddHttpClient("BackendAPI", client =>
{
    var apimEndpoint = builder.Configuration["BackendAPI:BaseUrl"];
    client.BaseAddress = new Uri(apimEndpoint ?? "https://your-apim-gateway.azure-api.net");
});

// Register API Service
builder.Services.AddScoped<ApiService>();

// Add services to the container.
builder.Services.AddRazorComponents()
    .AddInteractiveServerComponents();

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error", createScopeForErrors: true);
   
    app.UseHsts();
}

app.UseHttpsRedirection();

app.UseStaticFiles();
app.UseAntiforgery();

app.MapRazorComponents<App>()
    .AddInteractiveServerRenderMode();

app.Run();

// Authentication info class for tracking which credential method is being used
public class AuthenticationInfo
{
    public string Method { get; set; } = "Unknown";
}

Excluded Credentials

Enabled Credentials

This configuration prioritizes developer tooling credentials over secrets, enforcing passwordless authentication.

Get the complete source code from my GitHub Repo

Step 6: Testing with Visual Studio

From Visual Studio, make sure you are logged in and use the developer account. Run the application, navigate to the Weather page to test the APIM endpoint call,

Testing

Summary

We have seen how to implement secure, passwordless API authentication for Blazor Server applications calling Azure API Management (APIM) endpoints, using Microsoft Entra ID and the DefaultAzureCredential from the Azure Identity library. It addresses the security risks and operational overhead of traditional shared-secret approaches—including secret sprawl, lack of accountability, compliance challenges, and costly secret rotation—by adopting a Zero Trust security model where each developer authenticates using their own identity through Visual Studio or Azure CLI during development, while production workloads seamlessly transition to Azure Managed Identities without any code changes. The solution leverages App Roles defined in Microsoft Entra ID for fine-grained authorization, with APIM policies validating JWT tokens and enforcing role-based access control. This approach eliminates the need to share client secrets across development teams, provides full audit accountability for every API call, and reduces operational overhead to near-zero.

GitHub Repo