Authorize requests using external authorizer

This article shows an Azure API management policy sample that demonstrates how to secure API access by using an external authorizer encapsulating custom authentication/authorization logic. To set or edit a policy code, follow the steps described in Set or edit a policy. To see other examples, see policy samples.

Policy

Paste the code into the inbound block.

<!-- The policy defined in this file shows how to secure API access by using an external authorizer encapsulating custom authentication/authorization logic.

     In this sample we assume that:

        External authorizer evaluates only the information contained within the Authorization header. Alternatively, for example, a full copy of the incoming request can be forwarded to the authorizer by setting "mode" to copy in the send-request policy.

        External authorizer URL is stored in a named value called "authorizer-url" and is secured with a key included in a query parameter.

        External authorizer responds with a JSON object containing a property called "status" that is set to 200 if authorization was successful and 403 if it wasn't.
-->

<!-- Copy the following snippet into the inbound section and look at the trace window to see it work. -->

<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="response" timeout="10" ignore-error="false">
                    <set-url>{{authorizer-url}}</set-url>
                    <set-method>GET</set-method>
                    <set-header name="Authorization" 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["response"]).Body.As<JObject>()["status"].ToString())"/>
                <!-- 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="@((string)context.Variables["status"] == "403")">
                <return-response>
                    <set-status code="403" reason="Forbidden" />
                </return-response>
            </when>
        </choose>
    </inbound>
    <backend>
        <base/>
    </backend>
    <outbound>
        <base/>
    </outbound>
</policies>

Next steps

Learn more about APIM policies: