Securing your HTTP Request trigger in Microsoft Flow

With Microsoft Flow, you can start a Flow with REST by using the ‘When a HTTP request is received’ trigger. Since the HTTP endpoint generated by Flow is not secured, practically anyone with the (guessed) HTTP Post URL can trigger your Flow. In this blog, I will explain how you can secure this trigger to make sure your Flow cannot be triggered from unwanted sources.

As I wrote before in my Custom Actionable Messages with Microsoft Flow blog series, the ‘When a HTTP request is received’ will generate a HTTP Post URL when you first save your Flow:

From then on, this HTTP Post URL can be called from anywhere to trigger the Flow.

To show this, I created a real simple Flow that uses a ‘When a HTTP request is received’ trigger and a ‘Compose’ action that will contain the headers of the HTTP request trigger:

Trigger from Postman

After saving this Flow, my HTTP Post URL is generated. I can now call this URL using Postman which will trigger my Flow successfully:

When I look into my Flow history, I can see that the Request Header is filled. I can even see that the Flow is triggered from Postman:

Trigger from Flow

I only want this Flow to be triggered from another Flow in my Office 365 tenant and not from any other Flows or other (external) locations. To demonstrate how to do this, I created another Flow that calls the HTTP Post URL to trigger that Flow:

When I run this Flow, I can see that my other Flow will be triggered again. In the Request Header, I can see that the Flow is now triggered from Flow (Azure Logic Apps):

This is where it becomes interesting. When scrolling down, I can see the attribute ‘x-ms-workflow-name’ which contains the Flow ID of the Flow that triggers this Flow.

Allow the Flow to be triggered only from a specific other Flow

So, to make sure your Flow only runs when triggered from a specific Flow, you should perform a check on this ‘x-ms-workflow-name’ attribute by using a condition. The condition should check if the ‘x-ms-workflow-name’ attribute inside your Request Header is equal to the ID of the Flow that will trigger this Flow. Unfortunately, only the Request Header is available from your Dynamic content screen and not the attributed within your Request header:

That is because each Request header can be different (as you can see above: the Request Header from Postman was different than the one from Flow). To make use of the ‘x-ms-workflow-name’ attribute, you can switch to advanced mode and paste the following line into your window:

@equals(triggerOutputs()['headers']['x-ms-workflow-name'], '<FLOW ID>')

After that, you can switch back to basic mode (or leave it in advanced mode).

If the condition isn’t met, it means that the Flow isn’t triggered correctly, so we need to make sure the Flow doesn’t continue by using the Terminate action inside the ‘If no’ branch of your condition.

Now, when you create another Flow and perform a HTTP Post request to this Flow, you will see that this Flow will be terminated because the ID doesn’t match. When triggered from the correct Flow, it will continue:

What if this attribute doesn’t exist?

There is still one more thing to take in consideration: the ‘x-ms-workflow-name’ attribute does not always exist (e.g. when performing the request from Postman). If this attribute does not exist, the condition will fail:

So we need to check if this attribute is available within our Request Header and combine this with our first Condition.

To combine conditions, you do need the advanced mode for this because Flow does not provide a ‘drag-and-drop’ option for multiple conditions. We need to combine the following conditions into one:

@contains(triggerOutputs()['headers'], 'x-ms-workflow-name')

(by clicking ‘Edit in advanced mode’, your condition will be auto translated to this format)

and

@equals(triggerOutputs()['headers']['x-ms-workflow-name'], '<FLOW ID>')

both conditions have to be met, so we must use the @and() operand:

@and(
	contains(triggerOutputs()['headers'], 'x-ms-workflow-name'),
	equals(triggerOutputs()['headers']['x-ms-workflow-name'], '<FLOW ID>')
)

Make sure you put the check for the ‘x-ms-workflow-name’ attribute on top, otherwise it will check this attribute for the Flow ID before it even knows it doesn’t exist which will cause your Flow to fail.

With this final condition in place, we can check our Flow if it works:

Of course, theoretically it’s still possible to start your Flow from Postman if you build your Request header correcly but the chances are slim to nil that anyone from without your organisation knows all these attributes and their values.

Leave a Reply

Your email address will not be published. Required fields are marked *