Azure API Management response cache based on path parameter - azure

I need to implement business logic when the entire response is cached in APIM internally based on one path parameter. E.g. the URI is:
https://<apimhostname>/mycustomapi/v1/domains/<domain_id>/services
Based on domain_id path parameter, during the GET operation, I need to cache the entire response for specific domain_id and then return it back to the client during subsequent requests with given domain id. The number of possible domain_id values is less than 10.
I see vary-by-query-parameter policy which caches the entire response per value of specified query parameters. But, in fact, I do not have query parameters. On the other side, there is cache-store-value policy which allows storing the value by key. In my case the key is domain_id but I am not sure whether it is possible to store the entire response in that way.
Which approach is better to use in that case to cache the entire response based on path parameter?
Also, I have an Authorization header with an access token that is validated (iss and aud) on APIM side (validate-jwt policy), however, my backend validates the 'roles' claim. So when I reply back with a response from cache are there any ways to validate the role claim on APIM side?
Thanks.

Cache store policy (https://learn.microsoft.com/en-us/azure/api-management/api-management-caching-policies#StoreToCache) will do what you need to do:
<policies>
<inbound>
<base />
<cache-lookup vary-by-developer="true" vary-by-developer-groups="false"/>
</inbound>
<outbound>
<base />
<cache-store duration="3600" />
</outbound>
</policies>
The policy above takes request URL into account event if it's processed by the same APIM operation. I.e. request to https://<apimhostname>/mycustomapi/v1/domains/1/services and https://<apimhostname>/mycustomapi/v1/domains/2/services will be cached separately.

Related

Unauthorized Issue while Authenticating Azure Function with APIM

I created 1 Azure Function app (.NET Core 6) with a few functions.
Function Details:
ListUsers - GET Method with Routing(v1/listusers)
URL: https://samplefunction1.azurewebsites.net/api/v1/listusers
Step1: I registered a new Azure B2C Application for function app in B2C Tenant with Redirect URL as JWT MS (For testing purpose) and created B2C_1A_SIGNUP_SIGNIN custom policy in to get token.
Step2: I enabled Managed Identity (System Identity) ON for function app.
Step3: I provided authentication to azure function app with identity provider OPENID Connect and configured metadataURL, Client Id ,CLient Secret etc., of Step1 endpoint details
Step4: I Created APIM resource, enabled managed Identity(System Identity) and imported Function App and can see apim-Samplefunction host keys under function app keys.
Step4: I Created APIM resource and imported Function App added APIM Policy in under all operations and enabled CORS.
//Resource Id is taken from Step1 (ClientID)
<authentication-managed-identity resource="63b20196-e62b-4cf0-a60e-9e895ee5f1a2" />
Step5: I tested function URL in postman with authentication and host key and its success.
URL: GET METHOD
https://samplefunction1.azurewebsites.net/api/v1/listusers?code=<<HOST KEY Taken from Azure Function APP>>
Header:
Authorization : Bearer <<Token received from JWT.MS web page >>
APIM URL: When i tried same token with APIM Url, I am getting errorYou do not have permission to view this directory or page. with authentication-managed-identity policy with Azure Function App Client Id
I created another B2C application registration for APIM and used authentication-managed-identity policy with Azure APIM Client Id. At this time I am getting Internal Server Error.
My Queries:
Which client id I have to use to authorize APIM Url?
Does both APIM B2C & Azure Function B2C Registration is required?
Get Method
https://sample-apim-poc.azure-api.net/urlsuffix/v1/listusers
Headers:
Ocp-Apim-Subscription-Key:d40b5dfe106d40c0b234cec702173761
Authorization:Bearer <<Token received from JWT.MS web page >>
Ocp-Apim-Trace:true
Content-Type:application/json
Inbound Policy settings for All Operations:
<!--
IMPORTANT:
- Policy elements can appear only within the <inbound>, <outbound>, <backend> section elements.
- To apply a policy to the incoming request (before it is forwarded to the backend service), place a corresponding policy element within the <inbound> section element.
- To apply a policy to the outgoing response (before it is sent back to the caller), place a corresponding policy element within the <outbound> section element.
- To add a policy, place the cursor at the desired insertion point and select a policy from the sidebar.
- To remove a policy, delete the corresponding policy statement from the policy document.
- Position the <base> element within a section element to inherit all policies from the corresponding section element in the enclosing scope.
- Remove the <base> element to prevent inheriting policies from the corresponding section element in the enclosing scope.
- Policies are applied in the order of their appearance, from the top down.
- Comments within policy elements are not supported and may disappear. Place your comments between policy elements or at a higher level scope.
-->
<policies>
<inbound>
<base />
<cors allow-credentials="false">
<allowed-origins>
<origin>*</origin>
</allowed-origins>
<allowed-methods>
<method>GET</method>
<method>POST</method>
</allowed-methods>
</cors>
<authentication-managed-identity resource="13b20196-e62b-4cf0-a60e-9e895ee5f1a2" />
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>

How do I enforce a URL redirect when API URL suffix is not passed for my API Management

I have looked into the Rewrite URL for azure APIM but I don't know if this is the right approach as it looks like its more for the operations
I have an API Management instance (https://my-apim.azure-api.net) with 2 APIs:
AP1 1:- https://my-apim.azure-api.net/api1
AP1 2:- https://my-apim.azure-api.net/api2
I want to implement a case where the APIM redirects to the login page (e.g https://www.mywebsite.com/login) if my APIM is called without the API suffix (e.g. "/api2").
How would I go about this?
"All APIs" policy is executed for every request APIM receives. on-error section, among other cases, is executed if received request is not matched with any operation registered in APIM. See this doc on how to handle errors like 404 and others: https://learn.microsoft.com/en-us/azure/api-management/api-management-error-handling-policies, but in short, adding something like below into on-error at All APIs scope should do the trick:
<choose>
<when condition="#(context.LastError.Reason == "OperationNotFound")">
<return-response>
<set-status code="307" reason="Temporary Redirect"/>
<set-header name="Location" exists-action="override">
<value>https://www.mywebsite.com/login</value>
</set-header>
</return-response>
</when>
</choose>
You can implement additional checks to specifically target scenario when suffix is not provided, but above will cover all cases when URI in does not match any operation.
For your requirement, it seems rewrite-uri policy can't implement it. Please refer to my steps below:
1. I have two apis(which same with yours) in my APIM instance.
2. Then click "Add API" to create another api, we can name it with root. And set your login url as the "Web service URL"
3. After that, add operation in the newly created api root. Provide the display and just type / to the "URL" input box.
4. Now you can test request your APIM url https://my-apim.azure-api.net without api1/api2, it will redirect to your login page.

How to setup 405 Method Not Allowed for each of the methods in APIs using azure API Management

In azure API Management how to setup the 405 (Method not allowed) policy. I am using azure API management APIs and adding different policies like jwt validation, IP filtering, rate limit and all. But I couldn't find a way for add the 405 method not allowed in APIM. I want to setup this for each of the methods. That means I want to block the incoming unrecognized method requests from APIM. (eg: Get instead of POST (Throws 405 method not allowed from APIM). Currently APIM passes the wrong method to backend and it returns the 404 from the application. Anyone know how we can block the wrong request from APIM side and returns 405 instead of passing it to backend and returns 404?.
You could use a Control Flow policy along with the Context Variable on the Inbound policy of each Method to intercept any requests that don't match the defined http method and then use a Set Status policy to return a 405. So for a GET method something along the lines of:
<policies>
<inbound>
<choose>
<when condition="#(context.Request.Method.ToString() != "GET")">
<return-response>
<set-status code="405" reason="No Content" />
</return-response>
</when>
</choose>
<base />
</inbound>
... rest of policies
</policies>
If you've got multiple methods with the same path you might need to apply this at the API level rather than the Method level and make the condition equals methods not in use rather than not equal to method in use
To set this at the API level and check against a collection of methods not in use create a policy along the lines of:
<policies>
<inbound>
<choose>
<when condition="#{
ICollection<string> disallowedMethods = new List<string>() { "POST", "PUT" };
return disallowedMethods.Contains(context.Request.Method.ToString());
}">
<return-response>
<set-status code="405" reason="No Content" />
</return-response>
</when>
</choose>
<base />
</inbound>
... rest of policies
</policies>
The http methods not in use in this example are POST and PUT but you can change the list to whatever applies in your use case.

Azure API Management caching policy

In my Azure API Managemenent I'm defining a header based caching policy at API level.
The policy is quite simple:
<policies>
<inbound>
<check-header name="token" failed-check-httpcode="400" failed-check-error-message="Token header is missing" ignore-case="true" />
<cache-lookup vary-by-developer="false" vary-by-developer-groups="false" downstream-caching-type="none">
<vary-by-header>token</vary-by-header>
</cache-lookup>
<base />
</inbound>
<backend>
<forward-request />
</backend>
<outbound>
<cache-store duration="3600" />
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
This works ok in the case my downstream returns a 200 with a body - next request with same header token will hit the cache and the response will be returned from the API Management cache.
However is a error code is returned by the downstream (eg: 401 Unauthorized) that response is not cached by the API Management (confirmed by the tracing I've enabled on the API Management).
I was under the impression that whole responses are cached, but this doesn't seem to be the case...
Can somebody let me know if it's possible to cache responses also in case of unsuccessful http codes and if yes point me to some doc - I've been googling all day yesterday, but was unable to find more.
Thanks in advance!
This is by design. If you look into traces you should see message there "Backend service responed with the status code of 401 rather than 200 OK. Cache Store policy was not applied." The reasoning is that at APIM level we assume that non 200 responses are more transient than 200.
Say that client gets 401, goes on to do whatever to make sure that token is allowed to do the operation and retries the call. And still gets 401 from cache until cache expires.
That certainly may be added as an extra configuration option on a policy: http://aka.ms/apimwish
You could workaround that by using cache-lookup-value and cache-store-value. I.e. in the outbound section if you get 401 store some value in cache using cache-store-value to keyed with token. And in the inbound before cache-lookup do cache-lookup-value with token and see if you get value stored earlier. If you do you can generate 401 response right in place.

Azure API management retry policy 's condition not working

I am new to Azure API management. We are calling an external web api from Azure API management. If the web api is not able to communicate with the Azure API management, we are trying for a couple of retry before give up.
We have added a retry policy in the outbound node (policy) to check the response status of the backend node (policy). If the response status is other than 200 then we are trying to call the web api again after 10 seconds. We want to give 2 try before give up.
The problem is outbound node (policy) always execute send-request node (policy), even we just write
<retry condition="#(false)" count="2" interval="10" first-fast-
retry="false">
<send-request.....>
......
......
</send-request>
</retry>
How to write a condition in the retry policy so that it will get checked before executing the child nodes.
<outbound>
<base />
<retry condition="#(context.Response.StatusCode != 200)" count="2" interval="10" first-fast-retry="false">
<send-request mode="new" response-variable-name="responseVar" ignore-error="false">
<set-url>... </set-url>
<set-method>POST</set-method>
<set-body>...</set-body>
<set-header>...</set-header>
</retry>
</outbound>
Retry always executes its inner policies and only checks condition after first and subsequent executions. The idea is to check some result of inner policies to determine if they need to be executed again. To check condition upfront you'll have to wrap it into choose policy.

Resources