Azure Entra ID Authentication Provider
The Azure Entra ID (formerly Azure Active Directory) Authentication Provider enables JWT token validation against Microsoft’s identity platform. This provider automatically handles token validation using Azure’s public keys and supports both single-tenant and multi-tenant applications.
Overview
The Entra ID provider supports:
Automatic Key Rotation: Fetches public keys from Microsoft’s JWKS endpoint
Multi-tenant Support: Validates tokens from any Azure AD tenant
Claims Mapping: Maps Azure AD claims to user information
Group Integration: Supports Azure AD security groups and roles
Token Validation: Full JWT validation including signature, expiration, and issuer
Configuration
Basic Configuration
from auth_middleware import JwtAuthMiddleware
from auth_middleware.providers.authn.entra_id_provider import EntraIdProvider
from auth_middleware.providers.authn.entra_id_provider_settings import EntraIdProviderSettings
# Configure Entra ID settings
entra_settings = EntraIdProviderSettings(
tenant_id="your-tenant-id",
client_id="your-application-id",
issuer="https://sts.windows.net/your-tenant-id/",
)
# Create Entra ID provider
entra_provider = EntraIdProvider(settings=entra_settings)
# Add to FastAPI application
app.add_middleware(JwtAuthMiddleware, auth_provider=entra_provider)
Multi-tenant Configuration
For applications that need to accept users from any Azure AD tenant:
entra_settings = EntraIdProviderSettings(
tenant_id="common", # Accept from any tenant
client_id="your-application-id",
issuer="https://sts.windows.net/", # Generic issuer for multi-tenant
validate_tenant=False, # Skip tenant validation
)
Environment Variables
Required Environment Variables
# Required
ENTRA_TENANT_ID=your-tenant-id-or-common
ENTRA_CLIENT_ID=your-application-client-id
# Optional
ENTRA_ISSUER=https://sts.windows.net/your-tenant-id/
ENTRA_AUDIENCE=your-application-id-uri
ENTRA_VALIDATE_TENANT=true
Environment-based Configuration
import os
from auth_middleware.providers.authn.entra_id_provider_settings import EntraIdProviderSettings
def create_entra_settings():
return EntraIdProviderSettings(
tenant_id=os.getenv("ENTRA_TENANT_ID"),
client_id=os.getenv("ENTRA_CLIENT_ID"),
issuer=os.getenv("ENTRA_ISSUER"),
audience=os.getenv("ENTRA_AUDIENCE"),
validate_tenant=os.getenv("ENTRA_VALIDATE_TENANT", "true").lower() == "true",
)
Token Structure
Expected Entra ID Claims
Azure Entra ID tokens contain the following claims:
{
"aud": "your-application-id",
"iss": "https://sts.windows.net/tenant-id/",
"iat": 1640908800,
"exp": 1640995200,
"sub": "AAAAAAAAAAAAAAAAAAAAAMLkj3QiQZ-KiFQjQ",
"name": "John Doe",
"preferred_username": "john@company.com",
"email": "john@company.com",
"tid": "tenant-id",
"oid": "object-id",
"groups": ["group-id-1", "group-id-2"],
"roles": ["Role.Admin", "Role.User"],
"scp": "User.Read Profile.Read"
}
User Information Mapping
The provider maps Entra ID claims to user information:
class EntraIdUser:
id: str # From 'oid' claim
name: str # From 'name' claim
email: str # From 'email' or 'preferred_username'
tenant_id: str # From 'tid' claim
groups: List[str] # From 'groups' claim
roles: List[str] # From 'roles' claim
scopes: List[str] # From 'scp' claim (space-separated)
Azure AD App Registration
Application Setup
Register Application: Go to Azure Portal > App registrations > New registration
Configure Authentication: Set redirect URIs and supported account types
API Permissions: Add required Microsoft Graph permissions
Expose API: Configure application ID URI and scopes
App Registration Configuration
{
"displayName": "Your API Application",
"signInAudience": "AzureADMyOrg", // Single tenant
"identifierUris": [
"api://your-application-id"
],
"requiredResourceAccess": [
{
"resourceAppId": "00000003-0000-0000-c000-000000000000", // Microsoft Graph
"resourceAccess": [
{
"id": "e1fe6dd8-ba31-4d61-89e7-88639da4683d", // User.Read
"type": "Scope"
}
]
}
]
}
Integration Examples
Single-tenant Application
from fastapi import FastAPI, Depends
from auth_middleware import JwtAuthMiddleware, require_user, require_groups
from auth_middleware.providers.authn.entra_id_provider import EntraIdProvider
from auth_middleware.providers.authn.entra_id_provider_settings import EntraIdProviderSettings
app = FastAPI(title="Enterprise API")
# Single-tenant configuration
entra_settings = EntraIdProviderSettings(
tenant_id="12345678-1234-1234-1234-123456789012",
client_id="87654321-4321-4321-4321-210987654321",
issuer="https://sts.windows.net/12345678-1234-1234-1234-123456789012/",
)
app.add_middleware(
JwtAuthMiddleware,
auth_provider=EntraIdProvider(settings=entra_settings),
)
@app.get("/user/profile", dependencies=[Depends(require_user())])
async def get_user_profile(request):
user = request.state.current_user
return {
"user_id": user.id,
"name": user.name,
"email": user.email,
"tenant": user.tenant_id,
"roles": user.roles,
}
@app.get("/admin/users", dependencies=[Depends(require_groups(["Administrators"]))])
async def list_users(request):
# Only users in the "Administrators" group can access this endpoint
return {"message": "Admin access granted"}
Multi-tenant SaaS Application
# Multi-tenant configuration
entra_settings = EntraIdProviderSettings(
tenant_id="common",
client_id="your-saas-app-id",
issuer="https://sts.windows.net/",
validate_tenant=False,
)
app.add_middleware(
JwtAuthMiddleware,
auth_provider=EntraIdProvider(settings=entra_settings),
)
@app.get("/tenant/info", dependencies=[Depends(require_user())])
async def get_tenant_info(request):
user = request.state.current_user
return {
"tenant_id": user.tenant_id,
"user_count": await get_tenant_user_count(user.tenant_id),
"subscription": await get_tenant_subscription(user.tenant_id),
}
Token Acquisition
Client-side Token Acquisition
JavaScript/TypeScript example for acquiring tokens:
import { PublicClientApplication } from "@azure/msal-browser";
const msalConfig = {
auth: {
clientId: "your-application-id",
authority: "https://login.microsoftonline.com/your-tenant-id",
redirectUri: "http://localhost:3000"
}
};
const msalInstance = new PublicClientApplication(msalConfig);
// Acquire token
const tokenRequest = {
scopes: ["api://your-application-id/access_as_user"],
};
const response = await msalInstance.acquireTokenSilent(tokenRequest);
const accessToken = response.accessToken;
// Use token in API calls
const apiResponse = await fetch("/api/protected", {
headers: {
"Authorization": `Bearer ${accessToken}`
}
});
Server-side Token Acquisition
Python example using MSAL:
from msal import ConfidentialClientApplication
app = ConfidentialClientApplication(
client_id="your-application-id",
client_credential="your-client-secret",
authority="https://login.microsoftonline.com/your-tenant-id"
)
# Acquire token for API access
result = app.acquire_token_for_client(
scopes=["https://graph.microsoft.com/.default"]
)
if "access_token" in result:
access_token = result["access_token"]
Error Handling
Entra ID Specific Errors
from auth_middleware.exceptions import AuthenticationError
from fastapi import HTTPException
@app.exception_handler(AuthenticationError)
async def entra_auth_error_handler(request, exc):
error_details = {
"error": "authentication_failed",
"error_description": str(exc),
"tenant_hint": "Use your organization account"
}
if "tenant" in str(exc).lower():
error_details["error_uri"] = "https://docs.microsoft.com/azure/active-directory/develop/howto-convert-app-to-be-multi-tenant"
return JSONResponse(
status_code=401,
content=error_details
)
Best Practices
Security Recommendations
Validate Tenant: Always validate the tenant ID in single-tenant applications
Scope Validation: Validate that tokens contain required scopes
Group Membership: Use Azure AD groups for authorization
Token Caching: Implement proper token caching on the client side
Certificate Authentication: Use certificates instead of client secrets for production
Performance Optimization
Key Caching: The provider automatically caches Azure’s public keys
Connection Pooling: Use HTTP connection pooling for JWKS endpoints
Token Validation: Validate tokens locally to reduce Azure AD calls
Troubleshooting
Common Issues
- Invalid Issuer
Ensure the issuer in your configuration matches the token’s ‘iss’ claim
- Audience Mismatch
Verify that the client_id matches the token’s ‘aud’ claim
- Tenant Validation Failed
Check that the tenant_id matches the token’s ‘tid’ claim
- Groups Not Available
Ensure the application has the required Graph API permissions for group claims
API Reference
For more information about other authentication providers, see:
AWS Cognito Provider - AWS Cognito integration
JWT Authentication Provider - Generic JWT provider