Pages Menu
TwitterRssFacebook

Posted by on Jan 15, 2025 in Microsoft 365 Copilot

Declarative Agent Copilot now supports APIs secured with keys passed in query params or headers

Declarative Agent Copilot now supports APIs secured with keys passed in query params or headers

Azure Functions is my favourite and go-to tool for writing APIs. I love the complete separation of concerns and totally serverless model of it. I love the built-in invocation tracking and real-time logging you get for free.

However, the only way to secure an Azure Function for external use is with a code, passed along the URI as a parameter. This works fine most of the time.

Recently, though, I was doing some work with Copilot Declarative Agents (more on this coming soon). Briefly, this lets you tell Copilot to call your API in certain situations. When the Copilot orchestration thinks that your API can give the best answer it will construct an API call and populate any required values, and then use the return from the API call in its answer. It’s pretty cool.

Today, there is a limitation on how you can use protected APIs – they have to support one of the following authentication schemes:

  • OAuth2
  • Microsoft Entra / AD SSO
  • API key authentication

For my use case, OAuth2 and SSO don’t make sense: I wanted to make essentially anonymous calls, but make sure I’m not publishing an API that anyone can use.

Until recently though, the only way to use API key authentication was to pass the key in the Authorization header as a bearer token. Azure Functions doesn’t support this, so I was forced to use API Management Gateway as a proxy, which works really well for this. It’s fairly simple to look for the key in the Authorization header and move it to a URL parameter, by adding an inbound policy like this:

<set-query-parameter name="code" exists-action="override">               <value>@(context.Request.Headers.GetValueOrDefault("Authorization").Replace("Bearer ",""))</value>
</set-query-parameter>

Anyway, that isn’t really the purpose of this blog post, because now you don’t need to do that!

What’s new?

There has been an update to how API key authentication is supported. Now, keys can be passed:

  • as a bearer token in the Authorization header (as before)
  • as a custom header value
  • as a query parameter (this is what Azure Functions uses)

You specify which one of these you want to use in the OpenAPI schema. As a reminder, previously the only supported method was via the Authorization Bearer token, which was specified like this:

securitySchemes:
  BearerAuth:
    type: http
    scheme: bearer

But now, if you want to use a different header value, you can do like this:

securitySchemes:
  ApiKeyAuth:
    type: apiKey
    in: header
    name: X-API-KEY

If you want to use a query parameter, you can do like this:

securitySchemes:
  ApiKeyAuth:
    type: apiKey
    in: query
    name: api_key

In all these cases, and as before, the actual API key value needs to be registered in the Teams Developer Portal. You can do this manually, but if you create a Declarative Agent using Teams Toolkit then it’ll be done for you*.

*Gotcha! – Teams Toolkit Support

Depending on when you read this, you might run into problems, because the Teams Toolkit doesn’t currently support these new ways of passing tokens and will fail to use an OpenAPI schema that has them. The workaround for this is to change the schema to either use the Bearer token, or remove the security section altogether. Then, once the project has been created, you can add them back in. (Reminder: if you remove security altogether you’ll need to register API keys manually in the Teams Developer Portal, so this route is more work)

How does this work? Well, Teams Toolkit only creates the project, it doesn’t actually compile or provision it, that’s done by different code. This code supports the new values – it’s just the Toolkit that doesn’t. It’s a bit confusing, but hopefully just a temporary issue. A new version of the Teams Toolkit will likely support these new values.

Finally: just a tip to try and save you some debugging time! If you’re implementing this change to an existing agent that previously used something like API Management to convert the Bearer token to a URL parameter (as described above), and now you’re changing the URL back to the source, don’t forget that the host URL is also stored in the API key store in the Teams Developer Portal, not just in the OpenAPI schema. If you don’t change it there so that it matches the schema, you’ll get a cryptic “Fail” message in Copilot when it tries to invoke your API:

Written by Tom Morgan

Tom is a Microsoft Teams Platform developer and Microsoft MVP who has been blogging for over a decade. Find out more.
Buy the book: Building and Developing Apps & Bots for Microsoft Teams. Now available to purchase online with free updates.

Post a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.