Custom Authentication with Azure API Gateway
This article shows an Azure API management policy sample that demonstrates how to secure API access by using an external authenticator encapsulating custom authentication logic.
Before we look into the implementation of Custom authentication with Azure API Management, we shall look at API management.
Azure API Management (APIM) helps organizations publish APIs to external, partner, and internal developers to unlock the potential of their data and services. APIM enables you to create and manage modern API gateways for existing backend services hosted anywhere.
Benefits of using Azure API Management
- Use advanced security policies like JWT tokens, certificates, and other credentials
- Secures backend services by gating access with API keys.
- Prevents Denial of Service (DOS) attacks by using throttling
- And many other benefits
Architecture of APIM:
Azure API management is on top of our three back-end APIs (Orders API, Catalog API and Marketing APIs). Every call to these back-end APIs goes through the Azure API management service.
Purpose
We want to implement custom authentication in the project wherein authentication is done through a third-party authentication server. We want every incoming request to be validated against the authentication server, so each request should be redirected to https://auth.authprovider.com/auth and have the token validated. If the token is valid then let it proceed with the request or else send 401.
How to Secure API Access by Using External Authorizer Custom Authentication Logic?
External authenticator evaluates only the information contained within the Authorization header. Alternatively, for example, a full copy of the incoming request can be forwarded to the authenticator by setting “mode” to copy in the send-request policy.
External authenticator responds with the status that is set to 200 OK if authentication was successful and 401 Unauthorized if it wasn’t.
Getting started
To use API Management, we need to create APIs. Each API consists of one or more operations, and each API can be added to one or more products. To use an API, developers subscribe to a product that contains that API, and then they can call the API’s operation, subject to any usage policies that may be in effect using the Azure portal.
Create a New Service
From the Azure portal menu, select Create a resource. You can also select Create a resource on the Azure Home page.
– On the New page, select Integration > API Management
– In the API Management service page, enter settings
– It will take some minutes to create the instance of service. Once it is ready, we will be able to add APIs and apply policies on those APIs.
Go to your API Management Instance
– Once, the API Management service is deployed and status online. We will have the below screen and create new APIs.
– Now, I already have an Orders API which is open and does not have any authentication mechanism. So, anyone who has the URL of this API can call this and view the list of orders in the response. See the request below done using Postman:
– I need to add the custom authentication for this order endpoint. We need to add this API in Azure API management and add the policy to do the custom authentication. I have added the Orders API.
– To add the policy in the orders endpoint, we need to go to the Inbound Processing section and click on the icon highlighted in the above screenshot to set the policy.
– To authenticate the request using custom auth. We need an external authorizer’s URL (e.g. https://auth.authprovider.com/auth) where we need to redirect the request first and check the response code (200 OK) to verify the authentication of the request.
– Before redirecting the request to the authentication provider, – First, we need to check the Authentication header and if it is present then only redirect otherwise respond with 401 Unauthorized. See the policy below to verify this:
– After validating the request header, we need to forward the request to the authentication server using below code:
In the above screen, we have stored the response to the request using this:
response-variable-name=”authResponse”
We are extracting the response to get the status code of the authorizer’s response like:
<set-variable name=”status” value=”@(((IResponse)context.Variables[“authResponse”]).StatusCode)” />
So, we will have a status variable that contains the status code of the authentication server response. We need to check the status code that is set to 200 OK if authorization was successful and 401 Unauthorized if it wasn’t.
That’s it! We are done with our APIM policy and now the order endpoint request will be first redirected to the external authorizer endpoint and based on the status code of response it will call our endpoint.
Inbound policy: You can see the full policy code here.
<policies>
<inbound>
<base />
<!-- Ensure presence of Authorization header -->
<choose>
<when condition="@(!context.Request.Headers.ContainsKey("Authorization"))">
<return-response>
<set-status code="401" reason="Unauthorized" />
<set-header name="WWW-Authenticate" exists-action="append">
<value>@("Bearer realm="+context.Request.OriginalUrl.Host)</value>
</set-header>
</return-response>
</when>
</choose>
<!-- Check for cached authorization status for the subject
<cache-lookup-value key="@(context.Request.Headers.GetValueOrDefault("Authorization"))" variable-name="status" />
-->
<choose>
<!--
If a cache miss call external authorizer
-->
<when condition="@(!context.Variables.ContainsKey("status"))">
<!-- Invoke -->
<send-request mode="new" response-variable-name="authResponse" timeout="10" ignore-error="false">
<set-url>https://api.gruber-logistics.dev/user_company_links?CRM_company_code=86681</set-url>
<set-method>GET</set-method>
<set-header name="X-ZUMO-AUTH" exists-action="override">
<value>@(context.Request.Headers.GetValueOrDefault("Authorization"))</value>
</set-header>
</send-request>
<!-- Extract authorization status from authorizer's response -->
<set-variable name="status" value="@(((IResponse)context.Variables["authResponse"]).StatusCode)" />
<!-- Cache authorization result
<cache-store-value key="@(context.Request.Headers.GetValueOrDefault("Authorization"))" value="@((string)context.Variables["status"])" duration="5" />
-->
</when>
</choose>
<!-- Authorize the request -->
<choose>
<when condition="@((int)context.Variables["status"] == 200)" />
<otherwise>
<return-response>
<set-status code="401" reason="UnAuthorized" />
<set-body>@(((IResponse)context.Variables["authResponse"]).Body.As<JObject>(preserveContent: true).ToString())</set-body>
</return-response>
</otherwise>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Wrapping Up
This was quite extensive, right? We believe you found it resourceful and that you would use it in your projects. If you intend to keep things simple in terms of cloud services, you could reach out to a reliable cloud migration company like ours. We also specialize in Azure application development.