Azure API Management
provides different ways to control access to products, APIs and methods: these
are Subscriptions (https://docs.microsoft.com/en-us/azure/api-management/api-management-subscriptions), client certificates (https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-mutual-certificates-for-clients) and authentication mechanisms (https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-protect-backend-with-aad)
(special attention to
request pre-authorization with JWT token validation -https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-protect-backend-with-aad#configure-a-jwt-validation-policy-to-pre-authorize-requests).
In some custom
scenarios the authorization logic can be implemented using advanced Azure API
Management policies (https://docs.microsoft.com/en-us/azure/api-management/api-management-access-restriction-policies), but this option is tightly coupled to the
requirement to maintain access control logic within Azure API Management policy
as Azure API Management asset, which may lead to complicated CI/CD strategies
and processes and so on).
Another approach is to
“outsource” the access control logic to an external service, maintain it
separately from Azure API Management instance and use it from Azure API
management inbound policy (https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-policies) to control access to APIs or single methods
using appropriated policy scope (https://docs.microsoft.com/en-us/azure/api-management/api-management-howto-policies#apply-policies-specified-at-different-scopes).
The idea behind this
approach:
- Implement an access control [micro]service to make decision to allow or to deny access control to certain Azure API Management API or method (for example, Azure Function),
- Incoming request to an API method is received by Azure API Management instance,
- Inbound policy of the API or method extract from request information, essential to allow or to deny access to the API or method,
- Inbound policy issues request to the access control service (s. above) using information, extracted from request,
- Access control service returns response with either allowed or denied permission,
- Inbound policy uses the response from access control service to deny access or to pass it thru to the backend.
1 An API
client sends a request to an API method (including information for access
control) in Azure API Management instance.
2 The request is intercepted and analyzed by inbound policy, Azure API Management instance sends another request to an external access control service.
3 Access control service responds to Azure API Management instance with information about allowed or denied access.
4 If access is allowed, inbound policy passes the request thru the chain to backend service
4* If access is denied, according response is returned to the API client and request is not passed to backend.
2 The request is intercepted and analyzed by inbound policy, Azure API Management instance sends another request to an external access control service.
3 Access control service responds to Azure API Management instance with information about allowed or denied access.
4 If access is allowed, inbound policy passes the request thru the chain to backend service
4* If access is denied, according response is returned to the API client and request is not passed to backend.
Let’s look at a sample
implementation of this approach.
Assume an access
control service implemented as Azure Function:
#r "Newtonsoft.Json"
using System.Net;
using System.Text;
using Newtonsoft.Json;
//
// Sample implementation of access
control service
// for Azure APi Management API and
method protection
// making decision to allow or deny
access based on
// custom token value and API name.
//
// This function is intented to use
from Azure API Management
// inbound policy to implement fine
granulated custom access
// control to APIs and single methods
//
// HTTP GET request parameters:
// - token: custom access token string
// - API: API name
//
// Response JSON
// { "permitted": true |
false}
//
public
static async
Task Run(HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function
processed a request.");
// parse query parameter - access token value
string token = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key,
"token",
true) == 0)
.Value;
// parse query parameter - API name
string api = req.GetQueryNameValuePairs()
.FirstOrDefault(q => string.Compare(q.Key,
"API", true)
== 0)
.Value;
//
// This primitive access control logic must be
replaced by custom
//
var access = new
{permitted = (token=="permittedTokenValue"?true:false)};
return new
HttpResponseMessage(HttpStatusCode.OK) {
Content = new
StringContent(jsonObject, Encoding.UTF8, "application/json")
};
}
This primitive
function can be used as custom access control service using HTTP GET requests
with two query parameters: custom token and API name:
https:// /api/CheckAccess?token=mytoken&API=test
getting JSON response
either
{"permitted":false}
{"permitted":true}
Assume for simplicity
the API client sends custom access token information in a custom HTTP header
named “token” (in real life you may want to extract access control specific
information from JWT token as referenced above).
The inbound policy of
an API method with customized access control looks then like
<inbound>
<set-variable
name="token"
value="@(context.Request.Headers.GetValueOrDefault("token",""))"
/>
<send-request
mode="new"
response-variable-name="access"
timeout="20"
ignore-error="true">
<set-url>@("https://mbecho.azurewebsites.net/api/CheckAccess?token="+(string)context.Variables["token"]+"&API=protectedAPI")</set-url>
<set-method>GET</set-method>
<set-header
name="Authorization"
exists-action="override">
<value>whatever</value>
</set-header>
<set-header
name="Content-Type"
exists-action="override">
<value>application/x-www-form-urlencoded</value>
</set-header>
<set-body
/>
</send-request>
<choose>
<when
condition="@((bool)((IResponse)context.Variables["access"]).Body.As()[" permitted"]
== false)">
<return-response
response-variable-name="existing
response variable">
<set-status
code="401"
reason="Unauthorized"
/>
</return-response>
</when>
</choose>
<base
/>
</inbound>
Test the protected API
method with “permittedTokenValue” succeeds
The “Trace” tab in
Test pane of Azure API Management section in Azure portal contains detailed
info about flow of events and request and policy processing. The additional
latency caused by use of an external access control service in this sample is
under 50 msecs