Fetching Teams User Info from Microsoft Graph with TeamsFx SDK

Tomomi Imura 🐱 - Jul 29 '21 - - Dev Community

😘 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.

Screenshot - VS Code

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,
});


Enter fullscreen mode Exit fullscreen mode

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);
  })...
});


Enter fullscreen mode Exit fullscreen mode

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) => {
      ...
    });

  }
}


Enter fullscreen mode Exit fullscreen mode

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();
});


Enter fullscreen mode Exit fullscreen mode

Then, it runs the Dialog with the new message Activity.

πŸ“„ bot/dialogs/mainDialogs.js



const { Dialog, DialogSet, DialogTurnStatus, WaterfallDialog } = require("botbuilder-dialogs");


Enter fullscreen mode Exit fullscreen mode

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.

Microsoft Graph API

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");


Enter fullscreen mode Exit fullscreen mode

πŸ” 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"],
  })
);


Enter fullscreen mode Exit fullscreen mode

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 πŸ….

Screenshot - permissions dialog

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"]);


Enter fullscreen mode Exit fullscreen mode

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}`
  );
 ...



Enter fullscreen mode Exit fullscreen mode

Now this is the simplified flow of what you have just learned:

Teams bot flow

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:

Screenshot - bot showing user info

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();


Enter fullscreen mode Exit fullscreen mode

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;


Enter fullscreen mode Exit fullscreen mode

Also, try Graph Explorer to test out the REST API and visualize the response!

Screenshot - Graph Explorer


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!

πŸ“š Learn More

. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
Terabox Video Player