Is it possible to use the subscription-key query string parameter with Azure API Management SOAP-passthrough? - azure

We use API Management to expose several API's. One of the API's we expose is configured as a SOAP-passthrough API but we are facing some issues with it regarding authentication of APIM.
When we use the Ocp-Apim-Subscription-Key header for passing the query string it all works correct and the API is returning it's content correct.
When we use the subscription-key query string parameter the API is returning a 401 Unauthorized. I tested this behavior in Postman and changing the way of sending the subscription key is resulting in this behavior.
An implementation detail of this API is that it exposes an existing WSDL and routes this SOAPAction to an Azure Function via the policy. In the Application Insights of the function I can verify that the function is never invoked when I get a 401 but it is invoked when I get a successful call (using the header).
Is this normal behavior? Am I doing things wrong? Or is it a bug in APIM?

This might be an issue with the way we do routing for SOAP Passthrough. You will notice in the API setup that we add on a query parameter to identify the SoapAction that an operation will be matched to. It may be that your the api key query parameter is getting overwritten when adding the SoapAction parameter to the inbound request. I will investigate and let your know.

We currently use a workaround around this problem with the following policy. Instead of changing the backend-server url in the policy we send a request and set the response of that request as a response for this api. Below you can find our policy which is working with the subscription-key in the query string.
<policies>
<inbound>
<base />
<send-request mode="copy" response-variable-name="response" timeout="20" ignore-error="false">
<set-url>{{BackendServer_URL}}</set-url>
</send-request>
<!--return-response response-variable-name="reponse" /-->
<choose>
<!-- If StatusCode is not OK, return Unauthorized with the reason. -->
<when condition="#(((IResponse)context.Variables["response"]).StatusCode != 200)">
<return-response response-variable-name="reponse">
<set-status code="401" reason="Unauthorized" />
<set-body>#(((IResponse)context.Variables["response"]).Body.As<string>())</set-body>
</return-response>
</when>
<otherwise>
<return-response response-variable-name="reponse">
<set-status code="200" />
<set-header name="Content-Type" exists-action="override">
<value>text/xml; charset=utf-8</value>
</set-header>
<set-body>#(((IResponse)context.Variables["response"]).Body.As<string>())</set-body>
</return-response>
</otherwise>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>

Related

Can I call different backend on Azure API Management

I am trying to achieve that, when I call an APIM Endpoint based on the request headers It should different endpoint. for e.g when user call https://test.azure-api.net/testsvc-dev/api/test APIM should be able to send the request to https://testappv1:80/test or https://testappv2:80. right now I can see in the serviceURL, I can add only one. Is there any policy that I can use to do these kind of operations.
little more context: I am trying to access two different version of API on a single call. Instead of caller choosing which one to call,I am trying to make the APIM to decide based on the user.
You are probably looking for the set-backend-service policy.
Here an example from the Microsoft docs that changes the backend service based on a query parameter:
<policies>
<inbound>
<choose>
<when condition="#(context.Request.Url.Query.GetValueOrDefault("version") == "2013-05")">
<set-backend-service base-url="http://contoso.com/api/8.2/" />
</when>
<when condition="#(context.Request.Url.Query.GetValueOrDefault("version") == "2014-03")">
<set-backend-service base-url="http://contoso.com/api/9.1/" />
</when>
</choose>
<base />
</inbound>
<outbound>
<base />
</outbound>
</policies>
Certainly, you could adopt the sample and query for the desired headers ;-)

How to add custom header in Azure API Management before making a call to the backend

I have an Rest Api that is being exposed on Azure. The Azure Api once called calls a WCF Service in the backend.
Firstly I need to transform the JSON Request to XML. Also, To call the SOAP Service I need to add a Custom Header like this:-
<s:Header>
<AuthHeader xmlns="http://abc.security.service">
<UserID>UserID</UserID>
<Token>Token</Token>
</AuthHeader>
</s:Header>
How do I add an "Inbound Policy" that would transform the request to XML and inject the custom header ?
Any ideas or suggestions would be greatly appreciated !!
For this requirement, please refer to the policy in my APIM.
<policies>
<inbound>
<base />
<json-to-xml apply="always" />
<set-body>#{
string inBody = context.Request.Body.As<string>();
string requestBody = inBody.Replace("<Document>","").Replace("</Document>","");
string header = "<s:Header><AuthHeader xmlns=\"http://abc.security.service\"><UserID>UserID</UserID><Token>Token</Token></AuthHeader></s:Header>";
return header + requestBody;
}</set-body>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Please pay attention to the escape character in header, we need to use \" instead of ".

Handle JWT Token validation failute in Azure API Management Service

Hi I have added the jwt validate tag to one of the operation in my api management service as shown below
<!-- validate the bearerToken !-->
<validate-jwt header-name="Authorization" output-token-variable-name="jwtOutput">
<openid-config url="{{OPENIDConfigURL}}" />
<issuers>
<issuer>{{tokenIssuer}}</issuer>
</issuers>
</validate-jwt>
while testing the same with invalid JWT token I am getting internal server error but its not getting captured in my tag. Am i missing something here.
<on-error>
<base />
<return-response>
<set-status code="200" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
<set-body>{
{{FailResponse}};
}</set-body>
</return-response>
</on-error>
Any leads on how to capture the error when the provided jwt token is invalid (for any reason)
I believe you're getting an internal server error because your policy on-error is invalid.
Have a look to this thread and adjust your policy on-error. You can basically use the sample they have available in that link. The errors list shows you the list of errors you can expect from the validate-jwt policy.

Can Azure Api Management expose OpenAPI documentation?

We have some Azure Functions exposed through Api Management? Can Api Management expose a /swagger endpoint automatically, the same way the Swashbuckle package does for api's in Asp.Net.
Azure API management cannot automatically generate the swagger page. Azure API management only can provide you the API definition file. Then you can use other tools (such as Swagger UI) with the definition file to generate the page you need.
Besides, Azure API management has provided you the UI(https://youapimanagementname.portal.azure-api.net) to tell you how to use all the APIs.
You can expose your openapi documentation through the API itself.
The documentation of an API can be requested on
https://management.azure.com/subscriptions/[subscriptionid]/resourceGroups/[resourcegroupname]/Microsoft.ApiManagement/service/[servicename]/apis/[apiid]?export=true&format=openapi&api-version=2021-01-01-preview
Just create an additional operation (ex. openapi.yaml) on your API, call the url above through a custom policy and return the result. You can use the following policy
<policies>
<inbound>
<base />
<send-request mode="new" response-variable-name="result" timeout="300" ignore-error="false">
<set-url>#("https://management.azure.com/subscriptions/{{azure-subscriptionid}}/resourceGroups/{{azure-resourcegroup}}/providers/Microsoft.ApiManagement/service/" + context.Deployment.ServiceName + "/apis/" + context.Api.Id + "?export=true&format=openapi&api-version=2021-01-01-preview")</set-url>
<set-method>GET</set-method>
<authentication-managed-identity resource="https://management.azure.com/" />
</send-request>
<return-response>
<set-status code="200" reason="OK" />
<set-header name="Content-Type" exists-action="override">
<value>application/yaml</value>
</set-header>
<set-body>#((string)(((IResponse)context.Variables["result"]).Body.As<JObject>()["value"]))</set-body>
</return-response>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
More info can be found on https://www.devprotocol.com/2021/07/20/expose-openapi-documentation-on-azure-api-management.html

Forward requests to regional API based on a specific JWT claim

Is it possible to forward requests to regional API based on a specific JWT claim?
The platform I'm working on has one API per region, and our customers are required to know it in order to build the base request URL - e.g.: https://{region}.service.com
Unfortunately, the only reliable way to try and figure out which regional api to call automatically from Azure APIM (e.g.: calling a single endpoint at https://api.service.com), in our scenario, would be by analyzing a claim that always comes with the bearer token (which we already do at the APIM level.)
Has anybody had the need to do it this way? Thanks in advance!
APIM policy expressions along with "choose" policy allow you to create arbitrary processing logic: https://learn.microsoft.com/en-us/azure/api-management/api-management-policy-expressions.
Access to JWT is available as
context.Request.Headers.GetValueOrDefault("Authorization").AsJwt()
It returns Jwt object (look for it's properties on the same page above).
All this combined with "set-backend-service" policy should be sufficient to do the job.
Vitaly's answer was the key to figuring this one out. Here is the complete answer, in case anybody is looking for the same thing.
<policies>
<inbound>
<!-- Extract Token from Authorization header parameter -->
<set-variable name="token" value="#(context.Request.Headers.GetValueOrDefault("Authorization",string.Empty).Split(' ').Last().AsJwt())" />
<choose>
<when condition="#(context.Variables["token"] != null)">
<set-variable name="api_uri" value="#(((Jwt)context.Variables["token"]).Claims.GetValueOrDefault("api_uri", string.Empty))" />
<choose>
<when condition="#(context.Variables["api_uri"] != string.Empty)">
<set-backend-service base-url="#((string)context.Variables["api_uri"])" />
</when>
<otherwise />
</choose>
</when>
<otherwise />
</choose>
<base />
</inbound>
</policies>

Resources