ODC with Bots for Teams - Protect the Messaging Endpoint


So far, we have managed to get a simple bot up and running in OutSystems Developer Cloud (ODC). In fact, we've done much more than that. We built a messaging endpoint that can serve as a gateway to multiple bot applications in ODC, where each bot application can respond to inbound messages triggered as events individually. This setup is quite versatile and dynamic.
However, we are still missing two important implementations. First, our messaging endpoint is publicly available without any authorization checks. Essentially, anything could mimic an inbound message, and our bot would react, which is not good.
Second, in some scenarios, we need a user's credentials, not just their ID, to perform actions on behalf of that user in a bot implementation. Currently, through a Teams channel, we get the identifier of a user logged into Teams, but to use Microsoft Graph API operations on behalf of that user, we need the user to explicitly authenticate to retrieve their access token for Graph API operations.
In this tutorial, we will protect our messaging endpoint to ensure that we only handle requests initiated by Azure AI Bot services. In the next tutorial, we will explore how to authenticate a user and retrieve an access token to act on behalf of a user with the Graph API.
Demo Application
This article series includes a demo application called "ODC with Bots for Teams Demo," available on ODC Forge. Be sure to download the version of the application that matches each article in this series.
For this article, you need to install Version 0.4 from ODC Forge.
In the ODC Portal, go to Forge - All Assets.
Search for "ODC with Bots for Teams Demo".
Click on the Asset (Do not click on Install on the tile!).
Switch to the Version tab and click on Install next to Version 0.4.
Version 0.4 depends on other Forge components:
OAuthTokenExchange - An external logic library that helps retrieve access tokens easily.
Bot Framework Service API - A connector library for using Bot Connector API endpoints.
Authenticating Requests to the Messaging Endpoint
Any request from a configured channel is routed through the Azure AI Bot Connector service, which adds a signed JSON Web Token to the Authorization header of each request. The entire authentication and authorization process is well documented here: Authenticate requests with the Bot Connector API - Bot Service | Microsoft Learn. It involves the following steps:
Retrieve the token from the Authorization header.
Parse the token header to identify which key was used to sign the token.
Retrieve the public key for the identified key from the Bot Framework JSON Web Key Set URI.
Validate the token.
In ODC, we manage all of this with a custom OnAuthentication handler on the exposed Messaging endpoint.
OnAuthentication Handler
In the demo application under Logic - Integrations - REST, note that our MessagingEndpoint REST API authentication setting is now configured with Custom, which includes the OnAuthentication handler. OnAuthentication runs before a request reaches the actual endpoint.
Double-click the OnAuthentication handler to review the implementation.
The OnAuthentication handler performs several actions, starting with retrieving the value of the Authorization request header using Request_GetHeader from the HTTP module. The value is formatted as Bearer <encoded JSON web token>
. The JSON web token consists of three parts separated by dots. Each part is a base64 encoded JSON document.
Header - Contains information about the token and how the signature was calculated.
Payload - The token's payload with various key-value pairs, also known as claims.
Signature - The signature of the payload.
ParseAuthorizationHeader Action
This action performs some String_Split operations. First, it splits by a single space to divide the Authorization header value into the word "Bearer" and the encoded JSON web token.
Next, it splits the encoded JSON web token by a single dot to separate the Header, Payload, and Signature parts.
Note the individual IF conditions that raise an exception if the Authorization header is empty or if the split actions do not produce the expected results.
ParseJwtHeader Action
This action converts the encoded Header of the token into a JwtHeader structure and returns it. The Header structure includes the Key Identifier of the public key that we need to use to verify the token's signature.
GetJwksKey Action
This action retrieves the public key from the Bot Framework Identity Provider's JSON Web Key Set endpoint. The Bot Framework Identity Provider issues the signed token, and its URL is https://login.botframework.com
.
First we retrieve the Discovery document from the Identity Provider that contains the URI of the JSON Web Key Set endpoint using GetDiscoveryDocument from the OAuthTokenExchange Forge component.
Then we perform a GET request to the endpoint that returns the key sets using Request_SubmitGetRequest from the HTTP module and deserialize the response into a structure for easy filtering.
We filter the list of retrieved keys to the key identifier retrieved from the token header using a ListFilter action.
And finally we serialize the single key and return it.
ValidateToken Action
In ValidateToken, we use JWT_ReadToken from the Security module to check the signature with the retrieved public key.
If validation succeeds, the request is forwarded to the Messages endpoint for further processing.
Summary
At the end of this tutorial, we protected our messaging endpoint by validating the access token sent from the Azure AI Bot service. The great thing about this tutorial is that you can use this method whenever you need to validate a signed JSON web token.
Feel free to leave a comment with your questions or feedback.
Subscribe to my newsletter
Read articles from Stefan Weber directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by

Stefan Weber
Stefan Weber
As a seasoned Senior Director at Telelink Business Services EAD, a leading IT full-service provider headquartered in Sofia, Bulgaria, I lead the charge in our Application Services Practice. In this role, I spearhead the development of tailored software solutions using no-code/low-code platforms and cutting-edge cloud-ready/cloud-native solutions based on the Microsoft .NET stack. Throughout my diverse career, I've accumulated a wealth of experience in various capacities, both technically and personally. The constant desire to create innovative software solutions led me to the world of Low-Code and the OutSystems platform. I remain captivated by how closely OutSystems aligns with traditional software development, offering a seamless experience devoid of limitations. While my managerial responsibilities primarily revolve around leading and inspiring my teams, my passion for solution development with OutSystems remains unwavering. My personal focus extends to integrating our solutions with leading technologies such as Amazon Web Services, Microsoft 365, Azure, and more. In 2023, I earned recognition as an OutSystems Most Valuable Professional, one of only 80 worldwide, and concurrently became an AWS Community Builder.