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.
validate-jwt
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="https://login.microsoftonline.com/5bd0289f-3c17-4315-96eb-0e2116fa49fc/.well-known/openid-configuration" />
<audiences>
<audience>api://92e79087-fe31-4926-adb4-e5eef1114483</audience>
</audiences>
<issuers>
<issuer>https://sts.windows.net/5bd0289f-3c17-4315-96eb-0e2116fa49fc/</issuer>
</issuers>
</validate-jwt>
set-variable
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())" />
send-request
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">
<set-url>@($"https://func-apimfunction-ussc-dev.azurewebsites.net/api/token?key={context.Variables["key"]}&secret={context.Variables["secret"]}&client={context.Variables["client"]}&duration={context.Variables["duration"]}")</set-url>
<set-method>POST</set-method>
</send-request>
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.
set-header
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>
</set-header>
set-query-parameter
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">
<value>@((string)(context.Variables["client"]))</value>
</set-query-parameter>
<set-query-parameter name="duration" exists-action="override">
<value>@((string)(context.Variables["duration"]))</value>
</set-query-parameter>