1

I am exploring the Microsoft Bot Builder SDK to create a chat bot that integrates with MS Teams. Most of the provided samples do not have any authentication mechanisms and the samples that reference OAuth seem to do so for allowing the bot to access a resource using the on-behalf-of flow. Is correct way to think of the security model is that the bot should be considered public and any non-public information accessed is done from the context of the calling user?

Tedford
  • 2,842
  • 2
  • 35
  • 45
  • Are you looking for a generic answer, or is there a specific security solution you want? That is to say, does your question have a specific use case or will you accept an answer that addresses your question as you've asked it? What channel are you using, and who are you trying to restrict access to? – Kyle Delaney Jun 19 '20 at 18:50
  • @KyleDelaney The channel that I am using right now is teams. Ultimately I am interested in information/advice on the intent for how the bot service is be used. It seems that following the examples I just put an service on the internet which would respond to any well-formed request. Is there an authentication step that I am overlooking or is the bot intended to be a middleman automating access to different oauth services. – Tedford Jun 19 '20 at 21:13
  • So are you hoping to restrict the bot to users in specific tenants? Are you trying to keep from processing requests from sources other than Teams? – Kyle Delaney Jun 19 '20 at 23:01
  • @KyleDelaney Yes I would like to restrict the callers so that I can provide access to information/services that is more sensitive than the general public. At this point I am a noob attempting to grok how best to integrate this tool into our stack. If it requires oauth with a scope of User.Read just to get a profile and verify the tenant by hand then so be it. I just wanted to verify I am using the framework idiomatically and not shoehorn in concepts that were unintended. – Tedford Jun 22 '20 at 21:44

1 Answers1

1

The Bot Framework has three kinds of authentication/authorization to consider:

  1. Bot auth - Microsoft app ID and password
  2. Client auth - Direct Line secret/token, or various mechanisms for other channels
  3. User auth - OAuth cards/prompts/tokens

Unfortunately there's some inconsistency in the documentation about which is which, but I've just raised an issue about that here: https://github.com/MicrosoftDocs/bot-docs/issues/1745

In any case, there's no need to think of all bots as "public." The Bot Builder SDK authenticates both incoming messages and outgoing messages using its app ID and password. This means any unauthorized messages sent to the bot's endpoint will be rejected, and no other bot can impersonate yours.

In general you should have the user sign in if you want the bot to access secure information on the user's behalf. But since you mentioned wanting to restrict bot access to specific tenants, I can briefly explain how to do that. You can find middleware here that does it in C#, and here's a modified version of the code that I think improves on it by using a hash set instead of a dictionary:

public class TeamsTenantFilteringMiddleware : IMiddleware
{
    private readonly HashSet<string> tenantMap;
 
    public TeamsTenantFilteringMiddleware(IEnumerable<string> allowedTenantIds)
    {
        if (allowedTenantIds == null)
        {
            throw new ArgumentNullException(nameof(allowedTenantIds));
        }
 
        this.tenantMap = new HashSet<string>(allowedTenantIds);
    }
 
    public async Task OnTurnAsync(ITurnContext turnContext, NextDelegate next, CancellationToken cancellationToken = default(CancellationToken))
    {
        if (!turnContext.Activity.ChannelId.Equals(Channels.Msteams, StringComparison.OrdinalIgnoreCase))
        {
            await next(cancellationToken).ConfigureAwait(false);
            return;
        }
 
        TeamsChannelData teamsChannelData = turnContext.Activity.GetChannelData<TeamsChannelData>();
        string tenantId = teamsChannelData?.Tenant?.Id;
 
        if (string.IsNullOrEmpty(tenantId))
        {
            throw new UnauthorizedAccessException("Tenant Id is missing.");
        }
 
        if (!this.tenantMap.Contains(tenantId))
        {
            throw new UnauthorizedAccessException("Tenant Id '" + tenantId + "' is not allowed access.");
        }
 
        await next(cancellationToken).ConfigureAwait(false);
    }
}
Kyle Delaney
  • 11,616
  • 6
  • 39
  • 66