Locking Down a Payment API: How We Combined Azure API Management and Azure AD for Enterprise-Grade Security

Introduction
A fintech startup processing $15M/month in transactions, migrated to Azure serverless, their top priority was securing public APIs against breaches and DDoS attacks. While Azure AD authentication for Functions provided a solid foundation, they needed enterprise-grade security for their public endpoints.
By combining Azure API Management (APIM) as a secure gateway and Azure AD for identity, they achieved compliance, scalability, and ironclad security. Here’s how you can replicate this architecture.
Architecture Overview
Key Layers:
APIM: First line of defense (authentication, threat protection).
Azure AD: Identity provider for clients and internal services.
Azure Functions: Business logic with secondary validation.
Step 1: Securing the Gateway with Azure API Management
Why APIM?
WAF (Web Application Firewall): Blocks SQLi, XSS, and OWASP Top 10 attacks.
Rate Limiting: Throttles abusive clients.
Centralized Auth: Validate Azure AD tokens at the gateway level.
Implementation:
Deploy APIM (Developer or Basic tier).
Import Azure Functions as backend APIs.
Enable Azure AD Authentication for the API:
In APIM’s Security tab, add an Azure AD app registration as an identity provider.
Configure OAuth 2.0 with required scopes (e.g.,
payment.process
).
Apply Policies:
<!-- Validate JWT at APIM level -->
<validate-jwt header-name="Authorization" failed-validation-httpcode="401">
<openid-config url="https://login.microsoftonline.com/{tenant}/v2.0/.well-known/openid-configuration" />
<required-claims>
<claim name="aud" match="all">
<value>{api-client-id}</value>
</claim>
</required-claims>
</validate-jwt>
<!-- Rate limit: 100 calls/minute per client -->
<rate-limit-by-key calls="100" renewal-period="60" counter-key="@(context.Subscription.Id)" />
<!-- Block non-HTTPS requests -->
<when condition="@(context.Request.Url.Scheme != "https")">
<return-response>
<set-status code="403" reason="HTTPS Required" />
</return-response>
</when>
Result:
Blocked 12,000+ malicious requests in the first month.
Reduced unauthorized traffic to Functions by 95%.
Step 2: Azure AD Authentication for Clients
Setup:
Register Clients (SPA/mobile apps) in Azure AD.
Define Scopes: For e.g.,
Payment.Process
: For submitting transactions.Payment.Refund
: For refund approvals.
Clients Acquire Tokens:
// Example: SPA using MSAL.js
const msalConfig = {
auth: {
clientId: "{client-id}",
authority: "https://login.microsoftonline.com/{tenant-id}",
},
};
const request = { scopes: ["api://fintech-api/payment.process"] };
const token = await msal.acquireTokenSilent(request);
APIM Policy: Enforce scopes before forwarding requests:
<validate-jwt>
<!-- ... -->
<required-claims>
<claim name="scp" match="any">
<value>payment.process</value>
</claim>
</required-claims>
</validate-jwt>
Step 3: Backend Validation in Azure Functions
Defense-in-Depth: Even if APIM is breached, Functions perform secondary validation.
[FunctionName("ProcessPayment")]
public async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req)
{
// Extract token from header (validated by APIM, but verify again)
var token = req.Headers["Authorization"].ToString().Replace("Bearer ", "");
var principal = await ValidateTokenAndRolesAsync(token, new[] { "Payment.Admin" });
if (principal == null)
return new UnauthorizedResult();
// Access Key Vault securely via Managed Identity
var stripeKey = await _secretClient.GetSecretAsync("StripeApiKey");
// Process payment
}
Why Double Validation?
Mitigates risks of misconfigured APIM policies.
Enforces role-based access at the business logic layer.
Step 4: Securing Backend Services with Managed Identities
Problem:
Functions needed access to Azure SQL (transactions) and Key Vault (Stripe keys).
Solution:
Enable Managed Identity for the Function App.
Grant the identity:
- Key Vault Secrets User on the vault.
SQL Contributor on the database.
// Access SQL without credentials
var connection = new SqlConnection("Server=payflow-sql.database.windows.net; Authentication=Active Directory Managed Identity");
// Access Key Vault
var credential = new DefaultAzureCredential();
var secretClient = new SecretClient(new Uri(keyVaultUrl), credential);
Step 5: Audit and Compliance
APIM Analytics:
Track API usage, errors, and latency.
Identify suspicious clients with Request/Response Logging.
Azure Monitor Alerts:
Trigger emails/SMS for:
TooManyRequests
(APIM rate limits).FailedFunctionExecutions
(Application Insights).
PCI-DSS Compliance:
APIM WAF Logs: Prove protection against OWASP threats.
Azure AD Audit Logs: Track token issuance and admin changes.
Results
✅ Blocked 10,000+ malicious requests/month via APIM’s WAF and rate limits.
✅ Achieved PCI-DSS compliance with end-to-end encryption and audit trails.
✅ Zero hardcoded secrets (Managed Identities + Key Vault).
✅ 5x faster troubleshooting with centralized APIM and Application Insights logs.
Best Practices for APIM + Azure AD
Least Privilege Access:
Assign minimal roles to APIM (e.g., only forward to Functions).
Use granular Azure AD scopes (e.g.,
payment.read
,payment.write
).
Automate Policy Deployment:
- Use Azure DevOps to version-control APIM policies.
Test Security:
Run penetration tests with tools like OWASP ZAP.
Audit logs monthly for suspicious patterns.
Conclusion
For high-stakes APIs, Azure API Management and Azure AD are a powerhouse combo. APIM acts as your armored gateway, while Azure AD ensures only trusted identities get through. Add Functions with Managed Identities and Key Vault, and you’ve got a PCI-ready, scalable system.
Next: "Scaling to 1M Requests/Day: Auto-Scaling Azure Functions with APIM and Redis."
👉 Follow Azure Elevate for more production-tested codes/architectures.
Subscribe to my newsletter
Read articles from Anurag Dakalia directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by