url rewrite for the file extension in Azure APIM - azure

I need help in rewriting the url in Azure APIM, where my scenario is, I have this url https://azure.websites.net/api/hello.json in this url i'm trying to fetch the data from that file hello.json. But when i call this url i don't want to include .json extension, my expected url should look like this https://azure.websites.net/api/hello. when I call this url the data should be loaded in my client side. can anyone please help me on how to do this scenario. thanks in advance.

You can use inbound policies -rewrite URL template for the Azure APIM API to replace the client URL.
We can modify the rewrite URL template given in this Azure APIM GitHub Doc as per our requirement:
<rewrite-uri template="#(context.Request.OriginalUrl.Path.Replace(context.Api.Path+context.Operation.UrlTemplate.Replace("/*",""),""))" copy-unmatched-params="true" />
Place your original URL and the new URL in the below format:
<set-header name="Location" exists-action="override">
<value>#(context.Request.OriginalUrl.ToString().Replace("http://","https://"))</value>
</set-header>
This should be applied on the API Inbound Policies level given in above GitHub doc of Azure APIM Policies.

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>

Azure API Management response cache based on path parameter

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.

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.

Azure API Management/Portal: activate Mocking for "Try Out" in API Portal

I was wondering if there is the possibility to activate mocking for calls to an Azure API Management based API from the API Portal.
I don't want developers who browse the interface catalog and use the "try it" functionality to actually CRUD records from/in the backends. They should only receive a set of predefined data so they can develop against it without messing up the data in the backends.
Example
API is created on Azure API Management and productive.
This API is listed in the corresponding Azure API Portal -
a developer now finds that API and wants to try it out:
POST /SalesQuote would create a Sales Quotation in SAP. A developer uses the "try it" button in the developer portal to see the behaviour. The "send" button now actually creates a record in the backend.
Can this be prevented? If so - how? Would this be something that could be achieved within the API policy (i.e. mock data for origin = api portal url)?
The only other option I can think of would be disabling the try it feature - rather a bad solution.
EDIT: general "mocking-enabled" is of course for a productive API no option.
Thanks for your help!
Thanks to Nacho I researched the policy features more deeply and played around a bit. I came up with the following:
<inbound>
<base />
<choose>
<when condition="#(context.User.Groups.Any(Group => Group.Name == "developer-internal"))">
<mock-response status-code="201" content-type="application/json" />
</when>
</choose>
</inbound>
This should enable mocking only if the calling user is member of the group "developer-internal" (which are all developer portal users via AAD mapping).
As an alternative I am validating the re-routing option to our test backend instead of mocking the response which would look like this:
<inbound>
<base />
<choose>
<when condition="#(context.User.Groups.Any(Group => Group.Name == "developer-internal"))">
<!--mock-response status-code="201" content-type="application/json" /-->
<set-backend-service base-url="https://esb-test.example.com/restv2/CostObject" />
</when>
</choose>
</inbound>
Yes you can, you have to add a policy in the outbound of the API, normally I use it to expose apis that are in development process.
Go to the "Enable response mocking" part of this link:
https://learn.microsoft.com/en-us/azure/api-management/mock-api-responses

CORS Preflight request not working with Azure API Management

I have 2 Azure Websites (ASP.NET MVC 5 and ASP.NET WebApi 2). The MVC website has some jQuery which tries to post CORS request to the WebApi. It works just fine if it connects directly to the WebApi. However it doesn't work when trying to connect through the API Management.
The error I got in Chrome is:
XMLHttpRequest cannot load https://XXXXXX.azure-api.net/api/search. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'https://YYYYYY.azurewebsites.net' is therefore not allowed access.
I ruled out the problem being with the WebApi config, because as I said it works directly.
Below is my policy:
<policies>
<inbound>
<cors>
<allowed-origins>
<origin>*</origin>
<!-- allow any -->
</allowed-origins>
<allowed-headers>
<header>accept</header>
<header>accept-encoding</header>
<header>access-control-request-headers</header>
<header>access-control-request-method</header>
<header>connection</header>
<header>content-type</header>
<header>host</header>
<header>origin</header>
<header>referer</header>
<header>user-agent</header>
</allowed-headers>
<expose-headers>
<header>access-control-allow-headers</header>
<header>access-control-allow-origin</header>
<header>cache-control</header>
<header>content-length</header>
<header>date</header>
<header>expires</header>
<header>pragma</header>
<header>server</header>
<header>set-cookie</header>
<header>x-aspnet-version</header>
<header>x-powered-by</header>
</expose-headers>
</cors>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
</policies>
Any ideas?
CORS policy intended use is for the cases when your backend does not support CORS. In that case you can put this policy in and it will reply to OPTION requests without forwarding them to your backend. And you can use this policy to decide which origins/headers/methods to process for CORS.
Alternatively, if your backend already supports CORS and you see no benefit in handling CORS flow on APIM level you could just proxy the entire flow. For that to happen you should remove CORS policy and create a new operation in your API in APIM with OPTIONS method, so that OPTIONS requests would be forwarded to backend normally.
After spending many weeks fighting this issue, and coming up empty, I (with the help of Microsoft Support) discovered a bug in Azure API Management. If you have multiple APIs and one of them does not have an URL suffix (even if it's not the one that's failing), check it's CORS policy. The first API we put in Azure API Management didn't have any methods other than GET, so we set a CORS policy to only allow GET and OPTION. However, the bug that exists seems to make all inbound pre-flight requests route to the API without an URL suffix. So I was able to fix my issue by added a PUT (and POST for good measure) to the CORS policy of an unrelated API.
TL;DR: Check to make sure the CORS policy of the API with no URL suffix allows the method that's failing.
Hope this saves someone's sanity.
I was getting below preflight error from APIM and found that issue is that my policy <method>*</method> is actually not working. Changing the policy to explicitly add all the HTTP Verbs like below has resolved my issue.
<allowed-methods preflight-result-max-age="300">
<method>OPTIONS</method>
<method>GET</method>
<method>POST</method>
<method>PUT</method>
<method>PATCH</method>
<method>DELETE</method>
</allowed-methods>

Resources