Passing Input to Azure APIM Policy Fragment

Zaki Mohammed Zaki Mohammed
Jan 20, 2024 | 2 min read | 1740 Views | Comments

Azure APIM policy fragment enables you to define policy once and use it till eternity. In this article, we will understand the creation of a policy fragment and a way to pass input to them.

In continuation of this article Creating Validate JWT Policy in Azure APIM, where we have created policies related to validate-jwt as in bound or using fragment. Keeping that as a reference point let's begin with the showdown.

We can find a descriptive and beautifully documented guide about how to create a policy fragment from here , it covers below pointers:

While all this covers most of it, but one aspect is missed with such arrangement that we can't pass parameters to the fragments from policy. Let us bridge this gap by identifying a way to supply parameters to fragments.

For this we will first bring one fragment from the previous article and get started with:

<fragment>
	<!-- policy -->
	<validate-jwt 
		header-name="Authorization" 
		failed-validation-httpcode="401" 
		failed-validation-error-message="Unauthorized" 
		require-scheme="Bearer" 
		output-token-variable-name="output-token">

		<openid-config url="https://login.microsoftonline.com/contoso.onmicrosoft.com/v2.0/.well-known/openid-configuration/" />
		
		<audiences>
			<audience>f0047ad9-83fb-4a82-8167-d253b0dfb0c3</audience>
			<audience>08c06aea-ea55-48d4-9611-a43c53bf0955</audience>
		</audiences>

		<issuers>
			<issuer>https://login.microsoftonline.com/31537af4-6d77-4bb9-a681-d2394888ea26/v2.0</issuer>
		</issuers>
	</validate-jwt>
	
	<!-- custom-headers -->
	<choose>
		<when condition='@(!string.IsNullOrEmpty((string)((Jwt)context.Variables["output-token"]).Claims.GetValueOrDefault("username")))'>
			<set-header name="CO-Username" exists-action="override">
				<value>@((string)((Jwt)context.Variables["output-token"]).Claims.GetValueOrDefault("username"))</value>
			</set-header>
		</when>
	</choose>
	<choose>
		<when condition='@(!string.IsNullOrEmpty((string)((Jwt)context.Variables["output-token"]).Claims.GetValueOrDefault("email")))'>
			<set-header name="CO-Email" exists-action="override">
				<value>@((string)((Jwt)context.Variables["output-token"]).Claims.GetValueOrDefault("email"))</value>
			</set-header>
		</when>
	</choose>
</fragment>

Here, we are having validate-jwt policy with openid, audiences, issuers and we are setting 2 custom headers namely CO-Username and CO-Email with "CO-" as prefix (CO = CodeOmelet) which is obtained by the validate-jwt policy once verified with output variable "output-token".

<policies>
	<inbound>
		<base />
		<include-fragment fragment-id="ValidateJwtFragment" />
	</inbound>
	<backend>
		<base />
	</backend>
	<outbound>
		<base />
	</outbound>
	<on-error>
		<base />
	</on-error>
</policies>

Here, we have a policy code base where inside the inbound section we are including the previously created fragment "ValidateJwtFragment".

Now, if I want to supply an audience through the policy code base then I won't be able to as the "include-fragment" tag doesn't have such attribute. Instead, we can try a hack by creating a Policy Variable and accessing it in the fragment dynamically (using policy expression, C# way).

Let us see that in action:

<policies>
	<inbound>
		<base />
		<set-variable name="audience" value="938c1d33-5075-474c-b91a-1b539388ab54" />
		<include-fragment fragment-id="ValidateJwtFragment" />
	</inbound>
	<backend>
		<base />
	</backend>
	<outbound>
		<base />
	</outbound>
	<on-error>
		<base />
	</on-error>
</policies>

Here, we create a policy variable using "set-variable" tag with attribute name as "audience" and its associated value. We will now use this in the fragment using the policy expression.

<fragment>
	<validate-jwt>

		<openid-config />
		
		<audiences>
			...
			<audience>@(context.Variables.ContainsKey("audience") ? (string)context.Variables["audience"] : "f0047ad9-83fb-4a82-8167-d253b0dfb0c3")</audience>
		</audiences>

		<issuers>...</issuers>
	</validate-jwt>
	
	...
</fragment>

Here, we are using the Policy Expression to access the audience variable and supplying it to the audience tag, notice with the ternary else condition we are providing some default audience value as it needs to contain some value instead of empty audience. The ContainsKey method checks if the audience variable exists or not, the explicit typecasting is needed while accessing the variables.

By using the Policy Variable and Policy Expression we can easily supply and consume any input parameters to a policy fragment. Note that this approach might not be a fit for all situation, feel free to provide your input if you have come across some other way to do this.

Below are the references taken from Microsoft Azure documentation:


Zaki Mohammed
Zaki Mohammed
Learner, developer, coder and an exceptional omelet lover. Knows how to flip arrays or omelet or arrays of omelet.