π TL;DR
First of all, this tutorial became far longer than I initially intended, so I decided to include this quick summary of the article:
- There are various tools and SDKs for Teams app development so you need to pick the right & preferred tools!
- TeamsFx is a great tool when you build app that uses Microsoft Graph API for fetching data, such as basic user info
- TeamsFx handles the OAuth 2.0 authorization code grant flow, getting an access token, and use the token to make Graph API calls
- Use the Graph wrapper,
client.api(path).get
to make the call
Now, proceed to the main content!
In my previous tutorial, I showed you how to use a VS Code extension, Teams Toolkit to start building a Teams bot. In this tutorial, I walk you through the sample bot app from Toolkit and introduce you Microsoft Graph and TeamsFx. But before getting into the code, let me quickly explain about development tools.
π§ Teams Development Tools
In my other Microsoft Teams-related tutorials, I mentioned Teams SDK to build Tabs core user interface, Bot Framework to build bots and messaging extensions, Adaptive Cards for UI components in messages, and Teams Toolkit VSCode extension for quick scaffolding all types of Teams apps.
Some of the tools are not only created for Teams development, but for multiple Microsoft products. Here's a quick table of available tools for Teams:
Product | What it is | What it does for Teams app development | What else it is used for |
---|---|---|---|
Teams SDK (Teams.js) | Teams Client SDK | Tabs development (Front-end) | (Teams-specific) |
Bot Framework | Bot SDK | Teams bots development | Web & mobile chat, Skype, Facebook, Amazon Alexa, Slack, Twilio, etc. |
Adaptive Cards | Platform-agnostic UI snippets (JSON) | Rich-format messages & cards | Web & mobile UI, Outlook messages, etc. |
App Studio | Visual tool | Create & configure Teams app packages | (Teams-specific) |
Teams Toolkit | VS Code Extension | Register & configure app. App scaffold | (Teams-specific) |
TeamsFx | SDK & CLI | Implement identity and access to Graph resources | (Teams-specific) |
Microsoft Graph | REST API | Provides access to data stored across Microsoft 365 services, including Teams | Access & manage calendar, mail, oneDrive, etc. |
Developer Portal for Microsoft Teams (Preview) | Web tool | Configurations & references | (Teams-specific) |
There are more tools you can use for Teams, but I am not covering everything in this tutorial!
Usually during development, you would use a combination of some of them. For example, in this tutorial we are going to use Teams Toolkit, Bot Framework (that includes wrapper methods for Adaptive Cards), and TeamsFx (that includes Graph API wrappers).
Now, let's start off where we left off in the previous tutorial, Using VS Code Teams Toolkit Extension for Teams App Development.
π€ Using Microsoft Bot Framework
After you build and run the template code from the Toolkit, let's take a look at bot directory, where the bot code is stored.
Now, let's walk through the template code together quicklyβ
π bot/index.js
In index.js, notice botbuilder
(Microsoft Bot Framework) is imported.
This is where the adapter is created so that it allows your bot to communicate with the user and send responses.
const { BotFrameworkAdapter, ConversationState, MemoryStorage, UserState } = require("botbuilder");
...
const adapter = new BotFrameworkAdapter({
appId: process.env.BOT_ID,
appPassword: process.env.BOT_PASSWORD,
});
Teams Toolkit already has handled registering your bot to Azure Active Directory so you don't need to manually configure your Bot ID and password π
Also, Restify
is set up to HTTP server and routing HTTP requests.
const server = restify.createServer();
server.listen(process.env.port);
// Listen for incoming requests.
server.post("/api/messages", async (req, res) => {
await adapter.processActivity(req, res, async (context) => {
await bot.run(context);
})...
});
To simplify the tutorial, I am not explaining how to manage states, and what turn means. If you would like to learn the concept of bot, read How bot works.
π bot/teamsBot.js
The bot/teamsBot.js is the main entry point for the bot.
The TeamsBot
class is being created here. Its run
function is called by the adapter and routed to bot's activity logic through Restify
middleware (from index.js).
In the constructor, it overrides some of the TeamsActivityHandler
methods by extending it to customize bot behavior and message text.
class TeamsBot extends TeamsActivityHandler {
...
constructor(conversationState, userState, dialog) {
super();
...
this.onMessage(async (context, next) => {
...
});
this.onMembersAdded(async (context, next) => {
...
});
}
}
For example, methods overridden are onMessage
and onMembersAdded
in this sample. They register the event handlers for the message
event, emitted for every incoming message activity, and MembersAdded
event emitted when a new member is added to the conversation, respectively.
Message event
When a message is sent to bot from a user (like, show
command), onMessage
gets triggered.
this.onMessage(async (context, next) => {
await this.dialog.run(context, this.dialogState);
await next();
});
Then, it runs the Dialog
with the new message Activity.
π bot/dialogs/mainDialogs.js
const { Dialog, DialogSet, DialogTurnStatus, WaterfallDialog } = require("botbuilder-dialogs");
The dialogs library π¬ provides a state-based model to manage a long-running conversation with the user. A dialog performs a task that can represent conversational thread.
Okay, this was a quick walk-through of what some of the code does. Now, let's get to the main topic, TeamsFx and Graph.
πͺ What is TeamsFx?
TeamsFx is a framework, created to make the integrations of Microsoft Graph API and implementing identity and access with Teams easier. For example, it handles the OAuth 2.0 authorization code grant flow, getting an access token, and use the token to make Graph API calls.
Microsoft Graph API
So what is Microsoft Graph API, first of all? It is a REST API lets you to connect the data from Microsoft 365 services.
M365 platform holds various people-centric data and insights across the Microsoft cloud services, including Teams, Outlook, Calendars, etc. It means whenever you want to access to the data within your Teams app, you need to use Graph to access the data.
For example, in this sample app, when a user asks a bot to display the user's info with the show
command, the app make an API call to fetch the data from Graph.
πͺ Using TeamsFx to call Graph API
In bot/dialogs/mainDialogs.js, both TeamsFx and Graph libraries are imported:
const {createMicrosoftGraphClient, loadConfiguration, OnBehalfOfUserCredential, TeamsBotSsoPrompt} = require("@microsoft/teamsfx");
const { ResponseType } = require("@microsoft/microsoft-graph-client");
π User Authentication & Authorization
The app creates and authenticates a MicrosoftGraphClient
by calling loadConfiguration()
.
Then, a new instance of TeamsBotSsoPrompt
is added as a named dialog. TeamsBotSsoPrompt
is integrated with Bot Framework to simplify the authentication process for bots:
loadConfiguration();
dialogs.add(
new TeamsBotSsoPrompt("TeamsBotSsoPrompt", {
scopes: ["User.Read"],
})
);
The scopes
are the types of permission it requires to call Graph APIs for Teams. The resource-specific permissions are granular and define what an application can do within a specific resource.
There are various permission scopes that can read or write (create, edit, delete). For instance, User.Read
is the scope needed to read users info, as the name suggests. And to enable the permissions, the app must ask the user for their consent.
This operation creates a new prompt that leverages Teams Single Sign On (SSO) support for bot to automatically sign in user to receive OAuth token π .
Authentication and authorization are big topics here. I would need another tutorial to explain deeply about Microsoft identity platform. In the meantime, please read Authentication and authorization basics for Microsoft Graph.
π Calling Graph API
In the app, when a user sends the "show" command to the bot, the app calls an API from Graph to fetch the user info. (The app asks the user a permission by popping up a SSO window before fetching the user's info, as explained in the previous section.)
Every API call requires the access token π that has been acquired at the SSO sign-in process. (The token is attached in the Authorization header of the request).
Also, to access the Microsoft Graph API for Teams, you will need the Teams credentials to do authentication to create a Graph client object.
3 credential classes that are used to help simplifying authentication in TeamsFx:
-
TeamsUserCredential
- Teams current user's identity. Using this credential will request user consent at the first time. -
M365TenantCredential
- Microsoft 365 tenant identity. It is usually used when user is not involved like time-triggered automation job. -
OnBehalfOfUserCredential
- on-behalf-of flow. It needs an access token and you can get a new token for different scope.
For bot scenarios, we are using OnBehalfOfUserCredential
here:
const oboCredential = new OnBehalfOfUserCredential(tokenResponse.ssoToken);
const graphClient = createMicrosoftGraphClient(oboCredential, ["User.Read"]);
Now, you have authentication setup and an instance of Graph client, you can finally begin to make calls to the service!
This is how you can fetch the basic user information, then making the bot to send a message to Teams client:
const me = await graphClient.api("/me").get();
if (me) {
await stepContext.context.sendActivity(
`You're logged in as ${me.displayName} and your job title is: ${me.jobTitle}`
);
...
Now this is the simplified flow of what you have just learned:
Build and run on Teams client if you haven't. Try the show
command to ask the bot to display your info.
When everything goes well, this is how it looks like:
Ta-da π
π§Ί More with Graph
Try making more Graph calls with the api
method. All requests start with client.api(path)
and end with an action, get
or post
:
To get the Teams group the user have joined:
const profile = await graphClient.api("me/joinedTeams").get();
The response gives you a list of groups including the group IDs.
To get the members of a Teams group:
const profile = await graphClient.api("groups/{Teams group ID}/members").get;
Also, try Graph Explorer to test out the REST API and visualize the response!
I hope you enjoyed the tutorial. There are many things you would want to know, such as concepts of bots, OAuth, identity management, etc. but I tried not to make this tutorial too wordy by flooding with info, so I simplified everything by not diving deep into the details on purpose. But hopefully, this tutorial still made sense to you!
If you would like to dive deep, please check out the docs and code repos listed below!