How to expose a legacy API (with a legacy authentication system) using Azure API Management policies

My GitHub repo shows you how to call a custom “token” API endpoint programmatically as part of an Azure API Management policy for a different operation.

In this example, the custom API has a “token” endpoint that is required to call the actual API endpoint. This token endpoint requires some query parameters and returns a custom “token” in the body of the call. This custom token is then passed as the bearer token to the actual API endpoint.

We can use Azure API Management policies to call the custom “token” endpoint before allowing the call to reach the actual API endpoint. We will also force Azure AD authentication to the API call by validating the JWT token before allowing the call to continue.


Azure API Management policies

We need a few API Management policies to do all of the operations we need before the request reaches the actual API endpoint. These can be found in the /infra/api-management.bicep file.


First, we want to ensure all requests have a valid AAD JWT token before they can then call the API endpoint. We need to validate that they token came from my AAD tenant & that the audience is for the correct backend AAD service principal.

<validate-jwt header-name="Authorization" failed-validation-httpcode="401" failed-validation-error-message="Unauthorized. Access token is missing or invalid.">
    <openid-config url="" />


Next, we need to store the query parameters that were passed by the initial API call so we can use them later.

<set-variable name="key" value="@(context.Request.Url.Query["key"].First())" />
<set-variable name="secret" value="@(context.Request.Url.Query["secret"].First())" />
<set-variable name="client" value="@(context.Request.Url.Query["client"].First())" />
<set-variable name="duration" value="@(context.Request.Url.Query["duration"].First())" />


Next, we are going to send a request to the custom “token” endpoint. We need to pass the query parameters that were set in the previous step.

<send-request mode="new" response-variable-name="accessToken" timeout="20" ignore-error="true">

Note that the result of the custom “token” endpoint call is a JSON object that is stored in the accessToken variable.

  "Success": true,
  "Data": {
    "Token": "eyJLZXkiOiJhc2RmIiwiU2VjcmV0IjoiMTIzNCIsIlRva2VuU2VjcmV0IjoiYWJjMTIzIn0="
  "Errors": []

Obviously, there is nothing super secret in this “token” that we are returning. It contains the same query parameters & a secret value that will be checked by the backend API.


Now that we have the custom “token” in the accessToken variable, we need to set the bearer token as the query parameter for the actual API call.

<set-header name="Authorization" exists-action="override">
    <value>@($"Bearer {((IResponse)context.Variables["accessToken"]).Body.As<JObject>(preserveContent: true)["Data"]["Token"]}")</value>


Finally, we can clear the key & secret query parameters & use the client & duration query parameters in our final call the the actual API endpoint.

<set-query-parameter name="key" exists-action="delete" />
<set-query-parameter name="secret" exists-action="delete" />
<set-query-parameter name="client" exists-action="override">
<set-query-parameter name="duration" exists-action="override">


Related Posts

Leave a Reply

Your email address will not be published. Required fields are marked *