Sending a direct message to a user in Microsoft Teams

In this guide, we'll cover how to update a multi-tenant Microsoft Teams bot to send direct messages to Teams users using Knock. It assumes that you have already created a Microsoft Teams channel in Knock as outlined in the Microsoft Teams integration overview guide.

In this implementation, your application's users will connect their Microsoft Entra tenant to Knock and be able to send messages to individual users via direct message. To make this easier to implement, we'll use Knock's TeamsKit components to facilitate the OAuth flow.

Here's what we'll cover in this guide:

  • Modeling a multi-tenant application in Knock using Tenants
  • Adding required scopes to your Microsoft Teams app's manifest
  • Implementing a Microsoft OAuth flow using Knock's MsTeamsAuthButton component
  • Setting required User channel data when a Microsoft Teams bot is installed in a user's personal scope
  • Triggering a workflow with a user recipient to send a direct message to a Teams user

Prerequisites

Make sure your bot has been registered with Microsoft Bot Framework and is deployed to Azure. Knock does not manage deploying and configuring your bot. To set up Knock to send notifications as your bot, see How to connect to Teams with Knock.

Key concepts

TeamsKit connects multiple concepts in Knock to make it easier for your application's users to start using your Microsoft Teams integration. Tenants are a concept you'll see throughout the following docs that are foundational to how TeamsKit works, but might not be used in every implementation of Knock.

About tenants

Tenants in Knock are meant to represent groups of users who typically share the same resources. You might call these "accounts," "organizations," "workspaces," or something similar. In a typical implementation using TeamsKit, you'll store the ID of a Microsoft Entra tenant on a corresponding tenant in Knock.

If you already use Knock's tenant concept to power other 'account-based' features, you likely create tenants in Knock when an account or organization is created in your application. If you don't already use tenants in Knock, TeamsKit can create tenants for you on the fly if they don't already exist.

💡
Note: Our best-practice recommendation is that tenants in Knock should map one-to-one to whatever abstraction you use to model accounts, organizations, or workspaces. You can think of tenants as the top-level container within your data model that you use to power multi-tenancy in your application.

Merging channel data

In this implementation, we'll actually store the required channel data for an MsTeamsConnection across two different entities in Knock: a Tenant and an User. This is because we want to store the ms_teams_tenant_id for the Microsoft Entra tenant on the Knock Tenant and the ms_teams_user_id for the Microsoft Teams user on the Knock User.

When you trigger a workflow using this recipient and tenant, Knock will merge the channel data from the Tenant and the User to send the message to the correct Microsoft Teams user. By storing the ms_teams_tenant_id on the Knock Tenant, your customers only need to complete the OAuth flow once to connect their Microsoft Teams instances to Knock. From there, you can create UI that allows users to link their Microsoft Teams user ID to their Knock user ID or automate this process during user registration.

Adding required scopes to your app's manifest

In order for your bot to send direct messages to users in Microsoft Teams, you'll need to update your Microsoft Teams app's manifest so that it includes the personal scope for your bot.

In your manifest.json file, add personal to your bot's array of scopes:

Implementing TeamsKit

To facilitate the OAuth flow and channel selection process, we'll use Knock's TeamsKit components. TeamsKit is a set of React components that make it easier to build Microsoft Teams integrations in Knock. You can use TeamsKit to build a self-serve Microsoft Teams integration that allows your users to connect their Microsoft Teams instances to Knock.

Signing a user token

The only access you'll need to manage when using TeamsKit are grants for your users to interact with their Tenant in Knock. This is necessary because the user in this context is an end user in your application who does not have access to Knock as a member of the account. Therefore, these grants provide them elevated privileges to operate on specific resources using the API.

We've made it easy for you to tell Knock which resources your users should have access to by making it a part of their user token. In this section you'll learn how to generate these grants using the Node SDK and, if you're not using the SDK, how to structure them for other languages.

You'll need to generate a token for your user that includes access to the Knock tenant storing the Microsoft Entra tenant ID as well as any recipient objects storing Microsoft Teams channel data described in this reference on TeamsKit resource access grants.

Using the below example, you can quickly generate a token with the Node SDK.

You'll need to pass this token along with the public API key to the KnockProvider that wraps KnockMsTeamsProvider and the rest of your components. We recommend storing the generated user token in local storage so that your client application has easy access to it.

Adding provider components

In order to give your components the data they need, they must be wrapped in the KnockMsTeamsProvider. We recommend putting this high in your component tree so that any TeamsKit components that you use will be rendered within it. The Microsoft Teams provider goes inside of the KnockProvider. Your hierarchy will look like this:

The KnockMsTeamsProvider gives your components access to the status of the connection to your Microsoft Teams bot, so that they can all be in sync when a user is connecting, disconnecting, or experiencing a connection error.

Implementing the OAuth flow with MsTeamsAuthButton

The MsTeamsAuthButton component with MsTeamsAuthContainer

The MsTeamsAuthButton component with MsTeamsAuthContainer

Your users will give your Microsoft Teams bot access to their own Microsoft Entra tenants via the MsTeamsAuthButton. This button can be used on its own, or nested in the MsTeamsAuthContainer for a bigger visual footprint. Here's an example of how to use them:

The MsTeamsAuthButton maps a tenant in your product to a customer's Microsoft Entra tenant. This means in most cases you'll just need a single instance of the MsTeamsAuthButton.

Remember to consider which roles in your application can access the MsTeamsAuthButton component. Knock does not control access to the component. In most cases, you'll add this connect button/container in the settings area of your product.

⚠️
Note: The MsTeamsAuthButton component does not automatically install your Microsoft Teams bot into a user's personal scope. Your users will need to manually install your bot into their personal scope before you can send direct messages to them. Alternatively, provide instructions to your app's admins to preinstall your bot for all Microsoft Teams users in their organization.

Setting User channel data

Once your Microsoft Teams bot is installed in a user's personal scope, your bot's messaging endpoint will receive an installation update event. You can use this event to update the channel data associated with the Knock User.

In order to determine the user ID of the Knock User, you'll likely want to query your application's database based on the attributes of the Microsoft Teams user who installed your bot. The get user API of the Microsoft Graph API provides a convenient way to look up the email address of the user who installed your bot.

If you're using the Bot Framework SDK for JavaScript and the Microsoft Graph JavaScript client library with the Knock Node SDK, your code will look something like this:

Here, KNOCK_MS_TEAMS_CHANNEL_ID is the channel ID of your Microsoft Teams integration within Knock. getKnockUserIdFromEmailAddress is a function that you'll need to implement to look up a Knock User ID in your application's database for a given email address. How you get this ID depends upon your specific application.

Please keep in mind that if you intend to use the Microsoft Graph API in this fashion, you'll need to add the User.Read.All API permission when configuring your bot in Microsoft Entra.

Triggering a workflow

Once you have saved the user's Microsoft Teams user ID as channel data, you can trigger a workflow to send a message to that user's DM channel. Here's an example of how to trigger a workflow using the Knock Node SDK: