Add single sign-on to Teams app
Microsoft Teams provides single sign-on (SSO) function for an app to obtain signed in Teams user token to access Microsoft Graph and other APIs. Teams Toolkit facilitates the interaction by abstracting few of the Microsoft Azure Active Directory (Azure AD) flows and integrations behind simple APIs. This enables you to add SSO features easily to your Teams app.
Add SSO to Teams app for Microsoft Visual Studio Code
For apps that interact with the user in a chat, Team, or channel, SSO manifests as an Adaptive Card, which the user can interact with to invoke the Azure AD consent flow.
Enable SSO support
Teams Toolkit helps you to add SSO to the following Teams capabilities in Visual Studio Code:
- Tab
- Bot
- Notification bot: restify server
- Command bot
- Workflow bot
- Message extension
Add SSO using Visual Studio Code
You can perform the following steps to add SSO using Teams Toolkit in Visual Studio Code:
Open Visual Studio Code.
Select Teams Toolkit from the Visual Studio Code activity bar.
Select View How-to Guides in the DEVELOPMENT section.
From the dropdown list, select Develop Single Sign-On Experience in Teams. You'll be redirected to the respective How-to guide.
Development How-to Guide Develop Single Sign-on Experience in Teams How to Develop Single Sign-on Experience
Note
When SSO is enabled, Teams Toolkit by default provisions a single-tenant Azure AD app, which means only user and guest accounts in the same directory as your M365 account can sign in to your Teams app. For more information on supporting multi-tenant to update your TeamsFx project, see Multi-tenancy support for Azure AD app.
See also
Important
We've introduced the Teams Toolkit v5 extension within Visual Studio Code. This version comes to you with many new app development features. We recommend that you use Teams Toolkit v5 for building your Teams app.
Teams Toolkit v4 extension will soon be deprecated.
Microsoft Teams provides single sign-on (SSO) function for an app to obtain signed in Teams user token to access Microsoft Graph and other APIs. Teams Toolkit facilitates the interaction by abstracting some of the Microsoft Azure Active Directory (Azure AD) flows and integrations behind some simple APIs. This enables you to add SSO features easily to your Teams app.
Add SSO to Teams app for Microsoft Visual Studio Code
For apps that interact with the user in a chat, Team, or channel, SSO manifests as an Adaptive Card, which the user can interact with to invoke the Azure AD consent flow.
Enable SSO support
Teams Toolkit helps you to add SSO to the following Teams capabilities in Visual Studio Code:
- Tab
- Bot
- Notification bot: restify server
- Command bot
- Workflow bot
- Message extension
Add SSO using Visual Studio Code
You can perform the following steps to add SSO using Teams Toolkit in Visual Studio Code:
Open Visual Studio Code.
Select Teams Toolkit from the Visual Studio Code activity bar.
Select Add features under DEVELOPMENT.
- You can select View > Command Palette... to view Add features window.
Select Single Sign-On.
Add SSO using TeamsFx CLI
You can run teamsfx add sso
command in your project root directory.
Note
The feature enables SSO for all existing applicable capabilities. Follow the same steps to enable SSO if you add capability later to the project.
Customize your project using Teams Toolkit
The following table lists the changes Teams Toolkit makes to your project:
Type | File | Purpose |
---|---|---|
Create | aad.template.json under template\appPackage |
Azure AD application manifest represents your Azure AD app. template\appPackage helps to register an Azure AD app during local debug or provision stage. |
Modify | manifest.template.json under template\appPackage |
A webApplicationInfo object is added into your app manifest (previously called Teams app manifest) template. Teams requires this field to enable SSO. The change is in effect when you trigger local debugging or provisioning. |
Create | auth\tab |
Reference code, auth redirect pages, and README.md files are generated in this path for a tab project. |
Create | auth\bot |
Reference code, auth redirect pages, and README.md files are generated in this path for a bot project. |
Note
By adding SSO, Teams Toolkit doesn't change anything in the cloud until you trigger local debug. Update your code to ensure that SSO is working in the project.
Update your application to use SSO
To enable SSO in your application, follow these steps:
Note
These changes are based on the templates you scaffold.
Tab project
Copy
auth-start.html
andauth-end.htm
inauth\public
folder totabs\public\
. Teams Toolkit registers these two endpoints in Azure AD for Azure AD's redirect flow.Copy
sso
folder underauth\tab
totabs\src\sso\
.InitTeamsFx
: The file implements a function that initializes TeamsFx SDK and opensGetUserProfile
component after SDK is initialized.GetUserProfile
: The file implements a function that calls Microsoft Graph API to get user info.
Execute
npm install @microsoft/teamsfx-react
undertabs\
.Add the following lines to
tabs\src\components\sample\Welcome.tsx
to importInitTeamsFx
:import { InitTeamsFx } from "../../sso/InitTeamsFx";
Replace the following line:
<AddSSO />
with<InitTeamsFx />
to replace theAddSso
component withInitTeamsFx
component.
Bot project
Set up the Azure AD redirects
Move the
auth\bot\public
folder tobot\src
. This folder contains HTML pages that the bot app hosts. When SSO flow is initiated with Azure AD, it redirects you to the HTML pages.Modify your
bot\src\index
to add the appropriaterestify
routes to HTML pages.const path = require("path"); server.get( "/auth-*.html", restify.plugins.serveStatic({ directory: path.join(__dirname, "public"), }) );
Update your app
SSO command handler ProfileSsoCommandHandler
uses an Azure AD token to call Microsoft Graph. This token is obtained by using the signed in Teams user token. The flow is brought together in a dialog that displays a consent dialog if necessary.
Move
profileSsoCommandHandler
file underauth\bot\sso
folder tobot\src
.ProfileSsoCommandHandler
class is an SSO command handler to get user info with SSO token, follow this method and create your own SSO command handler.Open
package.json
file and ensure that TeamsFx SDK version >= 1.2.0.Execute the
npm install isomorphic-fetch --save
command in thebot
folder.For ts script, execute the
npm install copyfiles --save-dev
command in thebot
folder and replace following lines inpackage.json
:"build": "tsc --build && shx cp -r ./src/adaptiveCards ./lib/src",
with
"build": "tsc --build && shx cp -r ./src/adaptiveCards ./lib/src && copyfiles src/public/*.html lib/",
This copies the HTML pages used for auth redirect when building the bot project.
To make SSO consent flow work, replace the following code in
bot\src\index
file:server.post("/api/messages", async (req, res) => { await commandBot.requestHandler(req, res); });
with
server.post("/api/messages", async (req, res) => { await commandBot.requestHandler(req, res).catch((err) => { // Error message including "412" means it is waiting for user's consent, which is a normal process of SSO, shouldn't throw this error. if (!err.message.includes("412")) { throw err; } }); });
Replace the following options for
ConversationBot
instance inbot\src\internal\initialize
to add the SSO config and SSO command handler:export const commandBot = new ConversationBot({ ... command: { enabled: true, commands: [new HelloWorldCommandHandler()], }, });
with
import { ProfileSsoCommandHandler } from "../profileSsoCommandHandler"; export const commandBot = new ConversationBot({ ... // To learn more about ssoConfig, please refer teamsfx sdk document: https://docs.microsoft.com/microsoftteams/platform/toolkit/teamsfx-sdk ssoConfig: { aad :{ scopes:["User.Read"], }, }, command: { enabled: true, commands: [new HelloWorldCommandHandler() ], ssoCommands: [new ProfileSsoCommandHandler()], }, });
Register your command in the Teams app manifest. Open
templates\appPackage\manifest.template.json
, and add the following lines undercommands
incommandLists
of your bot:{ "title": "profile", "description": "Show user profile using Single Sign On feature" }
Add a new SSO command to the bot (Optional)
After successfully adding SSO in your project, you can add a new SSO command:
Create a new file such as
photoSsoCommandHandler.ts
orphotoSsoCommandHandler.js
inbot\src\
and add your own SSO command handler to call Graph API:// for TypeScript: import { Activity, TurnContext, ActivityTypes } from "botbuilder"; import "isomorphic-fetch"; import { CommandMessage, TriggerPatterns, TeamsFx, createMicrosoftGraphClient, TeamsFxBotSsoCommandHandler, TeamsBotSsoPromptTokenResponse, } from "@microsoft/teamsfx"; export class PhotoSsoCommandHandler implements TeamsFxBotSsoCommandHandler { triggerPatterns: TriggerPatterns = "photo"; async handleCommandReceived( context: TurnContext, message: CommandMessage, tokenResponse: TeamsBotSsoPromptTokenResponse, ): Promise<string | Partial<Activity> | void> { await context.sendActivity("Retrieving user information from Microsoft Graph ..."); const teamsfx = new TeamsFx().setSsoToken(tokenResponse.ssoToken); const graphClient = createMicrosoftGraphClient(teamsfx, ["User.Read"]); let photoUrl = ""; try { const photo = await graphClient.api("/me/photo/$value").get(); const arrayBuffer = await photo.arrayBuffer(); const buffer=Buffer.from(arrayBuffer, 'binary'); photoUrl = "data:image/png;base64," + buffer.toString("base64"); } catch { // Could not fetch photo from user's profile, return empty string as placeholder. } if (photoUrl) { const photoMessage: Partial<Activity> = { type: ActivityTypes.Message, text: 'This is your photo:', attachments: [ { name: 'photo.png', contentType: 'image/png', contentUrl: photoUrl } ] }; return photoMessage; } else { return "Could not retrieve your photo from Microsoft Graph. Please make sure you have uploaded your photo."; } } }
// for JavaScript: const { ActivityTypes } = require("botbuilder"); require("isomorphic-fetch"); const { createMicrosoftGraphClient, TeamsFx, } = require("@microsoft/teamsfx"); class PhotoSsoCommandHandler { triggerPatterns = "photo"; async handleCommandReceived(context, message, tokenResponse) { await context.sendActivity( "Retrieving user information from Microsoft Graph ..." ); const teamsfx = new TeamsFx().setSsoToken(tokenResponse.ssoToken); const graphClient = createMicrosoftGraphClient(teamsfx, ["User.Read"]); let photoUrl = ""; try { const photo = await graphClient.api("/me/photo/$value").get(); const arrayBuffer = await photo.arrayBuffer(); const buffer = Buffer.from(arrayBuffer, "binary"); photoUrl = "data:image/png;base64," + buffer.toString("base64"); } catch { // Could not fetch photo from user's profile, return empty string as placeholder. } if (photoUrl) { const photoMessage = { type: ActivityTypes.Message, text: "This is your photo:", attachments: [ { name: "photo.png", contentType: "image/png", contentUrl: photoUrl, }, ], }; return photoMessage; } else { return "Could not retrieve your photo from Microsoft Graph. Please make sure you have uploaded your photo."; } } } module.exports = { PhotoSsoCommandHandler, };
Add
PhotoSsoCommandHandler
instance tossoCommands
array inbot\src\internal\initialize.ts
:// for TypeScript: import { PhotoSsoCommandHandler } from "../photoSsoCommandHandler"; export const commandBot = new ConversationBot({ ... command: { ... ssoCommands: [new ProfileSsoCommandHandler(), new PhotoSsoCommandHandler()], }, });
// for JavaScript: ... const { PhotoSsoCommandHandler } = require("../photoSsoCommandHandler"); const commandBot = new ConversationBot({ ... command: { ... ssoCommands: [new ProfileSsoCommandHandler(), new PhotoSsoCommandHandler()] }, }); ...
Register your command in app manifest. Open
templates\appPackage\manifest.template.json
, and add following lines undercommands
incommandLists
of your bot:{ "title": "photo", "description": "Show user photo using Single Sign On feature" }
Message extension project
The sample business logic provides a handler TeamsBot
extends TeamsActivityHandler
and override handleTeamsMessagingExtensionQuery
.
You can update the query logic in the handleMessageExtensionQueryWithToken
with token, which is obtained by using the signed in Teams user token.
To make this work in your app:
Move the
auth\bot\public
folder tobot
. This folder contains HTML pages that the bot app hosts. When SSO flows are initiated with Azure AD, Azure AD redirects you to these pages.Modify your
bot\index
to add the appropriaterestify
routes to these pages.const path = require("path"); server.get( "/auth-*.html", restify.plugins.serveStatic({ directory: path.join(__dirname, "public"), }) );
Override
handleTeamsMessagingExtensionQuery
interface underbot\teamsBot
. You can follow the sample code in thehandleMessageExtensionQueryWithToken
to do your own query logic.Open
bot\package.json
, ensure that@microsoft/teamsfx
version >= 1.2.0Install
isomorphic-fetch
npm packages in your bot project.For ts, install
copyfiles
npm packages in your bot project, add or update thebuild
script inbot\package.json
as follows:"build": "tsc --build && copyfiles ./public/*.html lib/",
As you do it, the HTML pages used for auth redirect are copied when you build this bot project.
Update
templates\appPackage\aad.template.json
your scopes, which used inhandleMessageExtensionQueryWithToken
."requiredResourceAccess": [ { "resourceAppId": "Microsoft Graph", "resourceAccess": [ { "id": "User.Read", "type": "Scope" } ] } ]
Debug your app
Press F5 to debug your application. Teams Toolkit uses the Azure AD app manifest file to register an Azure AD app for SSO. For Teams Toolkit local debug functionalities, see how to debug your app locally.
Customize Azure AD app registration
The Azure AD app manifest allows you to customize various aspects of app registration. You can update the app manifest as needed. If you need to include more API permissions to access your desired APIs, see API permissions to access your desired APIs. To view your Azure AD app in Azure portal, see view Azure AD app in Azure portal.
SSO authentication concepts
The following concepts help you with SSO authentication:
Working of SSO in Teams
SSO authentication in Azure AD silently refreshes the authentication token to minimize the number of times users need to enter their sign in credentials. If users agree to use your app, they don't have to provide consent again on another device, as they're signed in automatically.
Teams tabs and bots have similar flow for SSO support. For more information, see:
Simplified SSO with TeamsFx
TeamsFx helps to reduce the developer tasks by using SSO and accessing cloud resources down to single-line statements with zero configuration.
With TeamsFx SDK, you can write user authentication code in a simplified way using following credentials:
- User identity in browser environment:
TeamsUserCredential
represents Teams current user's identity. - User identity in Node.js environment:
OnBehalfOfUserCredential
uses On-Behalf-Of flow and SSO token. - Application Identity in Node.js environment:
AppCredential
represents the application identity.
For more information about TeamsFx SDK, see:
See also
Feedback
Submit and view feedback for