Building a Microsoft Teams app: Posting a message to Teams on behalf of the current user

If you are building a Teams app and want to integrate with Teams channel messages, this post will be helpful to you. Specifically, we will be looking at posting a Teams channel message, from the app, on behalf of the currently logged in user.  For example, there could be a scenario where an event occurs within the Teams app and as a result a message needs to be posted to a Teams channel but instead of it coming from a bot, it needs to be posted by the logged in user’s account.

Let’s see how this can be done. 

1. Azure AD app and Permissions

First thing we need is an Azure AD app setup with the Microsoft Graph ChannelMessage.Send delegated permission. This permission is needed for posting messages to Teams using the current user credentials.

I should mention setting up the right permissions is part of a larger configuration in the Azure AD app needed for Single Sign On (SSO) setup in Teams app. You can see the full configuration here: Register your tab app with Azure AD – Teams | Microsoft Learn 

2. Current user’s id token from Teams JS SDK v2 

Once the permissions are setup, we need to setup our frontend so that it can grab the current user’s id token from Microsoft Teams. More info on Azure AD id tokens here: Microsoft identity platform ID tokens – Microsoft Entra | Microsoft Learn 

Although this token is available from Teams JS SDK v2, it cannot itself be used to make graph calls. We need to exchange it for a Microsoft Graph access token. For this we will send the id token to our backend:

const _postToTeams = async () => {
const idToken = await microsoftTeams.authentication.getAuthToken();
const teamsContext = await microsoftTeams.app.getContext();
const teamId = teamsContext.team?.groupId;
const channelId = teamsContext.channel?.id;
const message = “This message will be posted to Teams using Microsoft Graph”;
await fetch(`/PostToTeams?teamId=${teamId}&channelId=${channelId}&message=${message}`, {
method: ‘POST’,
headers: {
‘Accept’: ‘application/json’,
‘Content-Type’: ‘application/json’,
‘Authorization’: `Bearer ${idToken}`
}
});
}

view rawposttoteams.ts hosted with ❤ by GitHub

3. Getting Microsoft Graph delegated access token and posting message to Teams

It is recommended to do the token exchange as well as any further Graph calls from the backend of your app instead passing the Graph access token back to the frontend and making the calls from there:

public async Task<ActionResult> PostToTeams(string teamId, string channelId, string message)
{
var httpContext = _httpContextAccessor.HttpContext;
httpContext.Request.Headers.TryGetValue(“Authorization”, out StringValues assertion);
var idToken = assertion.ToString().Split(” “)[1];
var handler = new JwtSecurityTokenHandler();
var jwtSecurityToken = handler.ReadJwtToken(idToken);
string tenantId = jwtSecurityToken.Claims.First(claim => claim.Type == “tid”).Value;
GraphServiceClient graphServiceClient = await GetDelegatedGraphServiceClient(idToken, tenantId, new string[] {
“https://graph.microsoft.com/ChannelMessage.Send”
});
var chatMessage = new ChatMessage
{
Body = new ItemBody
{
Content = message
}
};
await graphServiceClient.Teams[teamId].Channels[channelId].Messages.Request().AddAsync(chatMessage);
return Ok();
}
private async Task<GraphServiceClient> GetDelegatedGraphServiceClient(string idToken, string tenantId, string[] scopes)
{
string clientId = _configuration.GetSection(“AzureAd:ClientId”)?.Value;
string clientSecret = _configuration.GetSection(“AzureAd:AppSecret”)?.Value;
IConfidentialClientApplication app = ConfidentialClientApplicationBuilder.Create(clientId)
.WithClientSecret(clientSecret)
.WithAuthority($”https://login.microsoftonline.com/{tenantId}”)
.Build();
UserAssertion assert = new UserAssertion(idToken);
var responseToken = await app.AcquireTokenOnBehalfOf(scopes, assert).ExecuteAsync();
string accessToken = responseToken.AccessToken.ToString();
var graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage
.Headers
.Authorization = new AuthenticationHeaderValue(“Bearer”, accessToken);
return Task.CompletedTask;
}));
return graphServiceClient;
}

view rawposttoteams.cs hosted with ❤ by GitHub

In the code we first graph the id token from the Authorization header, then exchange the id token for a Microsoft Graph access token, then finally we are able to make a Graph call to post a message to Teams as the current user.

Hope this helps!

This blog is part of Microsoft Teams Week!

About the Author

Working as a Technical Architect / Developer in London.

Reference

Deshpande, V., 2022, Building a Microsoft Teams app: Posting a message to Teams on behalf of the current user, Available at: https://www.vrdmn.com/2022/12/building-microsoft-teams-app-posting.html [Accessed on 13 March 2023]

Share this on...

Rate this Post:

Share: