I have been trying to change the cookie path of my Azure APIM API response. The response from the APIM looks like this Set-Cookie: ssnid=2d4cfdd62638436481130eb0fad7a889; path=/; secure; HttpOnly. I checked the transform policies available here but still haven't figured out a solution.
I haven't run this but this should generally work:
<policies>
<inbound>
<base />
</inbound>
<backend>
<base />
</backend>
<outbound>
<set-header name="Set-Cookie" exists-action="override">
<value>#{
var cookie = context.Response.Headers.GetValueOrDefault("Set-Cookie","");
return cookie.Replace("path=/;", "path=/myNewPath;");
}
</value>
</set-header>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Related
I'm trying to import a policy to azure api management using a bicep template as follows:
resource allOpsPolicy 'Microsoft.ApiManagement/service/apis/policies#2021-12-01-preview' = {
name: 'policy'
parent: apimapi_v1
properties: {
value: loadTextContent('all-ops-policy.xml')
format: 'xml'
}
}
The content of the all-ops-policy.xml file is as follows:
<policies>
<inbound>
<base />
<choose>
<when condition="#(context.Request.OriginalUrl.ToString().EndsWith("Public") == false)">
<rewrite-uri template="#(String.Concat(context.Request.OriginalUrl.ToString(),"/", "MapServer"))" copy-unmatched-params="false" />
</when>
<otherwise />
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
When run I get the following error:
One or more fields contain incorrect values: (Code: ValidationError)
'Public' is an unexpected token. Expecting white space.
Can anyone see where I'm going wrong?
The problem was the value of the format property of the allOpsPolicy resource.
I changed from
format: 'xml'
to
format: 'rawxml'
then it worked!
Thanks to Nisha for solving this one
Please replace the single quotes ` with double quotes ".
context.Request.OriginalUrl.ToString().EndsWith('Public') == false)
context.Request.OriginalUrl.ToString().EndsWith("Public") == false
It seems there's also one single quote too much:
String.Concat(context.Request.OriginalUrl.ToString(),'/'', 'MapServer')
String.Concat(context.Request.OriginalUrl.ToString(),"/", "MapServer")
Complete policy:
<policies>
<inbound>
<base />
<choose>
<when condition="#(context.Request.OriginalUrl.ToString().EndsWith("Public") == false)">
<rewrite-uri template="#(String.Concat(context.Request.OriginalUrl.ToString(),"/", "MapServer"))" copy-unmatched-params="false" />
</when>
<otherwise />
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
Issue is not reproducable:
Trying to make a policy for getting a bearer token trough a send-request sticking it in the Authorization header and then posting JSON data to the given back-end.
But when I test it within the test tab of Azure I always receive the same error:
Even when I add <forward-request timeout="60" follow-redirects="60"/> it does not work.
I also tried it without the follow-redirects which is defaulted to false but also no effect.
I am completly new to Azure so any help would be appreciated.
Here is my policy:
<policies>
<inbound>
<base />
<send-request ignore-error="true" timeout="20" response-variable-name="bearerToken" mode="new">
<set-url>{{AuthenticationServer}}</set-url>
<set-method>POST</set-method>
<set-header name="Content-Type" exists-action="override">
<value>application/x-www-form-urlencoded</value>
</set-header>
<set-header name="Authorization" exists-action="override">
<value>Basic {{Base64encodedusernamepassword}}</value>
</set-header>
<set-body>#{
return "grant_type=client_credentials";
}</set-body>
</send-request>
<set-header name="Authorization" exists-action="override">
<value>#("Bearer " + (String)((IResponse)context.Variables["bearerToken"]).Body.As<JObject>()["access_token"])</value>
</set-header>
<!-- Don't expose APIM subscription key to the backend. -->
<!--<set-header name="Ocp-Apim-Subscription-Key" exists-action="delete" /> -->
<set-backend-service base-url="{{BaseURI}}" />
</inbound>
<backend>
<forward-request timeout="60" follow-redirects="true" />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
The problem was that the endpoint was behind a private VNet so getting VPN access fixed the issue.
With Apim i'm trying to call a backend Api that needs a OAuth2 validation. This question are more or less similair to this: Azure API Management: Oauth2 with backend API
But there are no good answer here...
I have been reading alot about policies and caching.
But can't seem to set it up correctly. I hope to be able to cal the apim, and then the apim calls the backend api to get a token and with that token call an Api to get some output data.
I also found one where i had to setup some policies in the backend-part..
Can anyone help me set up the policies ?
my policy is like:
<policies>
<inbound>
<base />
<set-variable name="originBearer" value="#(context.Request.Headers.GetValueOrDefault("Authorization", "empty_token").Split(' ')[0].ToString())" />
<send-request ignore-error="true" timeout="20" response-variable-name="bearerToken" mode="new">
<set-url>{{lookupAccessTokenUrl}}</set-url>
<set-method>GET</set-method>
<set-header name="Content-Type" exists-action="override">
<value>application/x-www-form-urlencoded</value>
</set-header>
<set-body>#{
return "client_id={{HLR-app-client-id}}&scope={{HLR-scope}}&client_secret={{HLR-secret}}&assertion="+(string)context.Variables["originBearer"]+"&grant_type=urn:ietf:params:oauth:grant-type:client_credentials&requested_token_use=on_behalf_of";
}</set-body>
</send-request>
<set-variable name="requestResponseToken" value="#((String)((IResponse)context.Variables["bearerToken"]).Body.As<JObject>()["access_token"])" />
<set-header name="Authorization" exists-action="override">
<value>#("Bearer " + (string)context.Variables["requestResponseToken"])</value>
</set-header>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
I found the answer to my own Question :-)
I try to comment on each line, but if you take alle the code and put it together you get a policy to handle Oauth2 in a backend api.
In the inbound section, the cache-lookup-value
Assigns the value in cache to the context variable called “bearerToken”.
On first entry, the cache value will be null and the variable will not be
created.
<inbound>
<cache-lookup-value key="cacheAccessToken" variable-name="bearerToken" />
Create a variable that contains clientid and secret - needed to call the api
<set-variable name="user-password" value="{{HLR-Clientid}}:{{HLR-Secret}}"
/>
<choose>
Checks if the context variable collection contains a key called
“bearerToken” and if not found executes the code between the opening and closing
“” XML elements.
<when condition="#(!context.Variables.ContainsKey("bearerToken"))">
Initiates the request to the OAuth endpoint with a response
timeout of 20 seconds. This will put the response message into the variable
called “oauthResponse”
<send-request mode="new" response-variable-name="oauthResponse" timeout="20" ignore-error="false">
<set-url>{{lookupAccessTokenUrl}}</set-url>
<set-method>POST</set-method>
<set-header name="Content-Type" exists-action="override">
<value>application/x-www-form-urlencoded</value>
</set-header>
here you define your header Authorization and use the variable that contains clientid and password
<set-header name="Authorization" exists-action="override">
<value>#("Basic " + system.Convert.ToBase64String(Encoding.UTF8.GetBytes((string)context.Variables["user-password"])))</value>
</set-header>
<set-body>#("grant_type=client_credentials&scope={{HLR-Scope}}")</set-body>
</send-request>
Casts the response as a JSON object to allow the retrieval of the “access_token” value using an indexer and assigns it to the context variable “accessToken”.
<set-variable name="AccessToken" value="#((string)((IResponse)context.Variables["oauthResponse"]).Body.As<JObject>()["access_token"])" />
Store result in cache and where we add the contents of the variable “accessToken” into cache for a period of 3600 seconds.
<cache-store-value key="cacheAccessToken" value="#((string)context.Variables["AccessToken"])" duration="3600" />
Set the variable in a context-variable, then it can be used right now
<set-variable name="bearerToken" value="#((string)context.Variables["AccessToken"])" />
</when>
</choose>
<base />
</inbound>
<backend>
<!--Creates the request to the backend web service. Here we are placing the response from the web service into the variable called “transferWSResponse”.-->
<send-request mode="copy" response-variable-name="transferWSResponse" timeout="60" ignore-error="false">
<set-method>GET</set-method>
<!--Is the creating the “Authorization” header to be sent with the request.-->
<set-header name="Authorization" exists-action="override">
<value>#("Bearer " + (string)context.Variables["bearerToken"])</value>
</set-header>
<!--Removes the APIM subscription from being forwarded to the backend web service.-->
<set-header name="Ocp-Apim-Subscription-Key" exists-action="delete" />
<set-header name="Content-Type" exists-action="override">
<value>application/json</value>
</set-header>
</send-request>
</backend>
<outbound>
<!--Now we need to return the response message from the backend web service to the caller. This is done in the “<outbound>” policy section. Here we just simply return the value of the variable “transferWSResponse” back to the caller-->
<return-response response-variable-name="transferWSResponse" />
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
How to prevent large file requests using Azure APIM?
Example: Block any POST request having file size > 50MB
You can apply the following policy for all your APIs. For each POST request, the policy will check the body size, and if the size is above 50MB, it will return status 413 - Payload Too Large.
<policies>
<inbound>
<base />
<choose>
<when condition="#(context.Request.Method == "POST")">
<set-variable name="bodySize" value="#(context.Request.Headers["Content-Length"][0])" />
<choose>
<when condition="#(int.Parse(context.Variables.GetValueOrDefault<string>("bodySize"))<52428800)">
<!--let it pass through by doing nothing-->
</when>
<otherwise>
<return-response>
<set-status code="413" reason="Payload Too Large" />
<set-body>#{
return "Maximum allowed size for the POST requests is 52428800 bytes (50 MB). This request has size of "+ context.Variables.GetValueOrDefault<string>("bodySize") +" bytes";
}
</set-body>
</return-response>
</otherwise>
</choose>
</when>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
</policies>
There are policies called quota-by-Key and quota-by-subscription , which will help the users to block the calls that exceed the bandwidth specified. Please verify this link for more details.enter link description here
Is there any way to decrypt a bearer token in an API management policy in order to create a condition it's acr_values, for example a tenant.
Looking at the MS documentation it does not seem possible, I would be looking to achieve something like:
<when condition="#(context.Request.Headers["Authorization"] --DO MAGIC HERE-- .acr_values["tenant"] == "contoso" ">
<set-backend-service base-url="http://contoso.com/api/8.2/" />
</when>
Alternatively something like the example here but for setting the backed service:
http://devjourney.com/blog/2017/03/23/extract-jwt-claims-in-azure-api-management-policy/
Documentation I've read:
https://learn.microsoft.com/en-us/azure/api-management/api-management-transformation-policies#example-4
https://learn.microsoft.com/en-us/azure/api-management/policies/authorize-request-based-on-jwt-claims?toc=api-management/toc.json#policy
Did you try .AsJwt() method (https://learn.microsoft.com/en-us/azure/api-management/api-management-policy-expressions#ContextVariables):
<policies>
<inbound>
<base />
<set-header name="tenant" exists-action="append">
<value>#{
var jwt = context.Request.Headers.GetValueOrDefault("Authorization").AsJwt();
return jwt?.Claims.GetValueOrDefault("tenant") ?? "unknown";
}</value>
</set-header>
<choose>
<when condition="#(context.Request.Headers.GetValueOrDefault("tenant", "unknown") == "some-tenant" )">
<set-backend-service base-url="http://contoso.com/api/8.2/" />
</when>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
Also I'm not sure if you need it as a header to backend request, if not consider using set-variable policy.
Ok so I got it working in a very hacky way, you can set vales of the decrypted token in the header and then set conditions on that header.
<policies>
<inbound>
<base />
<set-header name="tenant" exists-action="append">
<value>#{
string tenant = "unknown";
string authHeader = context.Request.Headers.GetValueOrDefault("Authorization", "");
if (authHeader?.Length > 0)
{
string[] authHeaderParts = authHeader.Split(' ');
if (authHeaderParts?.Length == 2 && authHeaderParts[0].Equals("Bearer", StringComparison.InvariantCultureIgnoreCase))
{
Jwt jwt;
if (authHeaderParts[1].TryParseJwt(out jwt))
{
tenant = (jwt.Claims.GetValueOrDefault("tenant", "unknown"));
}
}
}
return tenant;
}</value>
</set-header>
<choose>
<when condition="#(context.Request.Headers.GetValueOrDefault("tenant", "unknown") == "some-tenant" )">
<set-backend-service base-url="http://contoso.com/api/8.2/" />
</when>
</choose>
</inbound>
<backend>
<base />
</backend>
<outbound>
<base />
</outbound>
<on-error>
<base />
</on-error>
A few years have passed since this has been answered, but as I found a less verbose solution, without actually modifying the request headers, i thought it would be nice to share for others:
<set-variable name="tenant" value="#{
var authHeader = context.Request.Headers.GetValueOrDefault("Authorization", "");
return authHeader.AsJwt()?.Claims.GetValueOrDefault("tenant", "");
}" />
...
<choose>
<when condition="#(context.Variables.GetValueOrDefault("tenant", "") == "your-tenant-id")">