Add single sign-on to your 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 some of the Microsoft Entra ID flows and integrations behind some simple APIs. This enables you to add SSO features easily to your Teams app.
Enable Single Sign-on in Teams Toolkit for Visual Studio
Teams provides SSO function for an app using the Microsoft Teams Toolkit for Microsoft Visual Studio.
Open Visual Studio.
Select Project > Teams Toolkit > Add Authentication Code.
Teams Toolkit helps you generate the authentication files in TeamsFx-Auth folder, including the app manifest (previously called Teams app manifest) template file for Microsoft Entra application and authentication redirect pages. Link the files to your Teams application by updating authentication configurations to ensure the SSO works for your application.
- In the Microsoft Entra app manifest file, specify the URIs (Uniform Resource Identifier) such as, the URI to identify the Microsoft Entra authentication app and the redirect URI for returning token.
- In the app manifest file, add the SSO application to link it with Teams application.
- Add SSO application information in Teams Toolkit configuration files in order to make sure the authentication app can be registered on backend service and start Teams Toolkit when you're debugging or previewing Teams application.
Teams tab application
Update Microsoft Entra app manifest:
TeamsFx-Auth/aad.manifest.template.json
file is a Microsoft Entra app manifest template. You can copy and paste this file to any folder of your project, and rename asaad.manifest.json
and take note of the path to this file. The following updates in the template to create/update a Microsoft Entra app for SSO:identifierUris
: It's used to uniquely identify and access the resource. Set the correct redirect URIs intoidentifierUris
to successfully identify this app. For more information, see identifierUris attribute."identifierUris":[ "api://${{TAB_DOMAIN}}/${{AAD_APP_CLIENT_ID}}" ]
replyUrlsWithType
: It lists registered redirect_uri values that Microsoft Entra ID accepts as destinations when returning tokens. Set necessary redirect URIs intoreplyUrlsWithType
to successfully return token. For more information, see replyUrlsWithType attribute."replyUrlsWithType":[ { "url": "${{TAB_ENDPOINT}}/auth-end.html", "type": "Web" } ]
Note
Use
${{ENV_NAME}}
to reference variables inenv/.env.{TEAMSFX_ENV}
."replyUrlsWithType":[ { "url": "${{TAB_ENDPOINT}}/auth-end.html", "type": "Web" }, { "url": "${{TAB_ENDPOINT}}/auth-end.html?clientId=${{AAD_APP_CLIENT_ID}}", "type": "Spa" }, { "url": "${{TAB_ENDPOINT}}/blank-auth-end.html", "type": "Spa" } ]
"name": It replaces the value with your expected Microsoft Entra app name.
Open your app manifest file, add
WebApplicationInfo
property with the value of your SSO app. For more information, see webApplicationInfo."webApplicationInfo": { "id": "${{AAD_APP_CLIENT_ID}}", "resource": "SAME_AS_YOUR_IDENTIFIERURIS" }
Note
Update the value of resource to your
identifierUris
configed in step 1, and use${{ENV_NAME}}
to reference envs inenv/.env.{TEAMSFX_ENV}
.Open the
appPackage/manifest.json
file, and add the following code:"webApplicationInfo": { "id": "${{AAD_APP_CLIENT_ID}}", "resource": "api://${{TAB_DOMAIN}}/${{AAD_APP_CLIENT_ID}}" }
Update the
teamsapp.yml
file and theteamsapp.local.yml
file.Add Microsoft Entra related changes and configs into your
yml
files:Add
aadApp/create
underprovision
: Create new Microsoft Entra apps used for SSO. For more information, see aadApp/create.Add
aadApp/update
underprovision
: Update your Microsoft Entra app with Microsoft Entra app manifest in step 1. For more information, see aadApp/update.Update
file/createOrUpdateJsonFile
: Add the following environment variables when you debug locally:- ClientId: Microsoft Entra app client ID.
- ClientSecret: Microsoft Entra app client secret.
- OAuthAuthority: Microsoft Entra app oauth authority.
For more information, see file/updateJson.
In both the
teamsapp.yml
file and theteamsapp.local.yml
file add the following code under theprovision
to create Microsoft Entra app.- uses: aadApp/create with: name: "YOUR_AAD_APP_NAME" generateClientSecret: true signInAudience: "AzureADMyOrg" writeToEnvironmentFile: clientId: AAD_APP_CLIENT_ID clientSecret: SECRET_AAD_APP_CLIENT_SECRET objectId: AAD_APP_OBJECT_ID tenantId: AAD_APP_TENANT_ID authority: AAD_APP_OAUTH_AUTHORITY authorityHost: AAD_APP_OAUTH_AUTHORITY_HOST
Note
Replace the value of "name" with your expected Microsoft Entra app name.
Add the following lines under
provision
to configure Microsoft Entra app with Microsoft Entra app template in the step 1.- uses: aadApp/update with: manifestPath: "YOUR_PATH_TO_AAD_APP_MANIFEST" outputFilePath : ./build/aad.manifest.${{TEAMSFX_ENV}}.json
Note
Replace the value of
manifestPath
with the relative path of Microsoft Entra app manifest noted in step 1. For example:./aad.manifest.json
In the
teamsapp.local.yml
file:Add the following code under
provision
to add Microsoft Entra related configs to local debug service.- uses: file/createOrUpdateJsonFile with: target: ./appsettings.Development.json appsettings: TeamsFx: Authentication: ClientId: ${{AAD_APP_CLIENT_ID}} ClientSecret: ${{SECRET_AAD_APP_CLIENT_SECRET}} InitiateLoginEndpoint: ${{TAB_ENDPOINT}}/auth-start.html OAuthAuthority: ${{AAD_APP_OAUTH_AUTHORITY}}
Update Infra Microsoft Entra related configs need to be configured in your remote service. The following example shows the configs on Azure Webapp.
- TeamsFx__Authentication__ClientId: Microsoft Entra app client ID.
- TeamsFx__Authentication__ClientSecret: Microsoft Entra app client secret.
- TeamsFx__Authentication__OAuthAuthority: Microsoft Entra app oauth authority.
Example for TeamsFx Tab template.
Open
infra/azure.parameters.json
and add following lines intoparameters
:"tabAadAppClientId": { "value": "${{AAD_APP_CLIENT_ID}}" }, "tabAadAppClientSecret": { "value": "${{SECRET_AAD_APP_CLIENT_SECRET}}" }, "tabAadAppOauthAuthorityHost": { "value": "${{AAD_APP_OAUTH_AUTHORITY_HOST}}" }, "tabAadAppTenantId": { "value": "${{AAD_APP_TENANT_ID}}" }
Open the
infra/azure.bicep
file, find the code:param location string = resourceGroup().location
Update the code as:
param tabAadAppClientId string param tabAadAppOauthAuthorityHost string param tabAadAppTenantId string @secure() param tabAadAppClientSecret string
In the
infra/azure.bicep
file, find the code:resource webApp 'Microsoft.Web/sites@2021-02-01' = { kind: 'app' location: location name: webAppName properties: { serverFarmId: serverfarm.id httpsOnly: true siteConfig: { appSettings: [ { name: 'WEBSITE_RUN_FROM_PACKAGE' value: '1' } ] ftpsState: 'FtpsOnly' } } }
Update the code as:
resource webApp 'Microsoft.Web/sites@2021-02-01' = { kind: 'app' location: location name: webAppName properties: { serverFarmId: serverfarm.id httpsOnly: true siteConfig: { ftpsState: 'FtpsOnly' } } } resource webAppConfig 'Microsoft.Web/sites/config@2021-02-01' = { name: '${webAppName}/appsettings' properties: { WEBSITE_RUN_FROM_PACKAGE: '1' TeamsFx__Authentication__ClientId: tabAadAppClientId TeamsFx__Authentication__ClientSecret: tabAadAppClientSecret TeamsFx__Authentication__InitiateLoginEndpoint: 'https://${webApp.properties.defaultHostName}/auth-start.html' TeamsFx__Authentication__OAuthAuthority: uri(tabAadAppOauthAuthorityHost, tabAadAppTenantId) } }
Update
appsettings.json
andappsettings.Development.json
files for Microsoft Entra related configs needs to be configured to your .NET project settings:TeamsFx: { Authentication: { ClientId: AAD app client id ClientSecret: AAD app client secret, InitiateLoginEndpoint: Login Endpoint, OAuthAuthority: AAD app oauth authority } }
Note
You can use use
$ENV_NAME$
to reference envs in local/remote service.Example for TeamsFx Tab template.
Open
appsettings.json
andappsettings.Development.json
files, and update the code:"TeamsFx": { "Authentication": { "ClientId": "$clientId$", "ClientSecret": "$client-secret$", "InitiateLoginEndpoint": "$TAB_ENDPOINT$/auth-start.html", "OAuthAuthority": "$oauthAuthority$" } }
Your environment is ready and you can update your code to add SSO to your Teams app. You can find samples:
- TeamsFx SDK: https://www.nuget.org/packages/Microsoft.TeamsFx/
- Sample Code: under
TeamsFx-Auth/Tab
Example for TeamsFx Tab template.
Create
Config.cs
and update the code as:using Microsoft.TeamsFx.Configuration; namespace {{YOUR_NAMESPACE}} { public class ConfigOptions { public TeamsFxOptions TeamsFx { get; set; } } public class TeamsFxOptions { public AuthenticationOptions Authentication { get; set; } } }
Note
You need to replace
{{YOUR_NAMESPACE}}
with your namespace name.Move the
TeamsFx-Auth/Tab/GetUserProfile.razor
file toComponents/
.Add the
GetUserProfile
component to your razor page, for example:<h1>Hello, World</h1> <GetUserProfile />
Open the
Program.cs
file, find the code:builder.Services.AddScoped<MicrosoftTeams>();
and update the code as:
var config = builder.Configuration.Get<ConfigOptions>(); builder.Services.AddTeamsFx(config.TeamsFx.Authentication); ```
Note
You need to exclude the sample code under the
TeamsFx-Auth
file to avoid build failure by adding following code into the.csproj
file:<ItemGroup> <Compile Remove="TeamsFx-Auth/**/*" /> <None Include="TeamsFx-Auth/**/*" /> <Content Remove="TeamsFx-Auth/Tab/GetUserProfile.razor"/> </ItemGroup> ``` * Download `auth-start.html` and `auth-end.html` files from [GitHub Repo](https://github.com/OfficeDev/TeamsFx/tree/dev/templates/csharp/sso-tab/wwwroot) to `{ProjectDirectory}/wwwroot`.
To check the SSO app works as expected, run the
Local Debug
in Visual Studio.You can also run the app in cloud by selecting the
Provision in the cloud
and thenDeploy to the cloud
.
Teams bot application
Update Microsoft Entra app manifest in the
TeamsFx-Auth/aad.manifest.template.json
file.You can copy the file to any folder of your project, and rename as the
aad.manifest.json
file and note the path to this file for later reference. Make the following updates in the template to create or update a Microsoft Entra app for SSO.identifierUris
: Used to uniquely identify and access the resource. You need to set correct Redirect URIs into "identifierUris" for successfully identify this app. For more information, see identifierUris attribute.
Example for TeamsFx Bot Template:
"identifierUris":[ "api://botid-${{BOT_ID}}" ]
Note
You can use
${{ENV_NAME}}
to reference variables in theenv/.env.{TEAMSFX_ENV}
file.replyUrlsWithType
: It lists registered redirect_uri values that Microsoft Entra ID accepts as destinations when returning tokens. You need to set necessary Redirect URIs into "replyUrlsWithType" for successfully returning token. For more information, see replyUrlsWithType attribute.
Example:
"replyUrlsWithType":[ { "url": "https://${{BOT_DOMAIN}}/bot-auth-end.html", "type": "Web" } ]
Note
You can use use
${{ENV_NAME}}
to reference envs in theenv/.env.{TEAMSFX_ENV}
file.Example:
"replyUrlsWithType":[ { "url": "https://${{BOT_DOMAIN}}/bot-auth-end.html", "type": "Web" } ]
- "name": Replace the value with your expected Microsoft Entra app name.
Update app manifest.
- A
WebApplicationInfo
object needs to be added into your app manifest to enable SSO in the Teams app. For more information, see webApplicationInfo.
For example: open your app manifest template, and append the following object in app manifest:
"webApplicationInfo": { "id": "${{AAD_APP_CLIENT_ID}}", "resource": "SAME_AS_YOUR_IDENTIFIERURIS" }
Note
You need to update the value of resource to your
identifierUris
configured in step 1.i, and use${{ENV_NAME}}
to reference envs inenv/.env.{TEAMSFX_ENV}
.Example for TeamsFx Bot template:
Open the
appPackage/manifest.json
file, and add the following property in the app manifest file:"webApplicationInfo": { "id": "${{AAD_APP_CLIENT_ID}}", "resource": "api://botid-${{BOT_ID}}" }
- You can register your command under
commands
incommandLists
of your bot:
{ "title": "YOUR_COMMAND_TITLE", "description": "YOUR_COMMAND_DESCRIPTION" }
Example for TeamsFx Bot template:
{ "title": "show", "description": "Show user profile using Single Sign On feature" }
Remember to delete the previous 'helloWorld' command since it isn't used.
- Also add bot domain to
validDomain
:
"validDomains": [ "${{BOT_DOMAIN}}" ]
- A
Update
teamsapp.yml
andteamsapp.local.yml
files: Microsoft Entra related changes and configs needs to be added into youryml
files:Add
aadApp/create
underprovision
for creating new Microsoft Entra apps used for SSO. For more information, see available actions in Teams Toolkit.Add
aadApp/update
underprovision
for updating your Microsoft Entra app with Microsoft Entra app manifest in step 1. For more information, see aadApp/update.Update
file/createOrUpdateJson
File for adding the following environment variables during local debug:- ClientId: Microsoft Entra app client ID.
- ClientSecret: Microsoft Entra app client secret.
- OAuthAuthority: Microsoft Entra app oauth authority. For more information, see file/updateJson.
Example for TeamsFx Bot template
In both
teamsapp.yml
andteamsapp.local.yml
files:- Add the code under
provision
to create Microsoft Entra app.
- uses: aadApp/create with: name: "YOUR_AAD_APP_NAME" generateClientSecret: true signInAudience: "AzureADMyOrg" writeToEnvironmentFile: clientId: AAD_APP_CLIENT_ID clientSecret: SECRET_AAD_APP_CLIENT_SECRET objectId: AAD_APP_OBJECT_ID tenantId: AAD_APP_TENANT_ID authority: AAD_APP_OAUTH_AUTHORITY authorityHost: AAD_APP_OAUTH_AUTHORITY_HOST
Note
Replace the value of "name" with your expected Microsoft Entra app name.
Add the code under
provision
to configure Microsoft Entra app with Microsoft Entra app template in the step 1.- uses: aadApp/update with: manifestPath: "./aad.manifest.json" outputFilePath : ./build/aad.manifest.${{TEAMSFX_ENV}}.json
Note
Replace the value of "manifestPath" with the relative path of Microsoft Entra app manifest noted in step 1. For example, './aad.manifest.json'
In the
teamsapp.local.yml
file:Update
file/createOrUpdateJsonFile
underprovision
to add Microsoft Entra related configs to local debug service.- uses: file/createOrUpdateJsonFile with: target: ./appsettings.Development.json appsettings: BOT_ID: ${{BOT_ID}} BOT_PASSWORD: ${{SECRET_BOT_PASSWORD}} TeamsFx: Authentication: ClientId: ${{AAD_APP_CLIENT_ID}} ClientSecret: ${{SECRET_AAD_APP_CLIENT_SECRET}} OAuthAuthority: ${{AAD_APP_OAUTH_AUTHORITY}}/${{AAD_APP_TENANT_ID}} ApplicationIdUri: api://botid-${{BOT_ID}} Bot: InitiateLoginEndpoint: https://${{BOT_DOMAIN}}/bot-auth-start
Update Infra Microsoft Entra related configs to configure remote service. The following example shows the configs on Azure Webapp.
- TeamsFx__Authentication__ClientId: Microsoft Entra app client ID.
- TeamsFx__Authentication__ClientSecret: Microsoft Entra app client secret.
- TeamsFx__Authentication__OAuthAuthority: Microsoft Entra app oauth authority.
- TeamsFx__Authentication__Bot__InitiateLoginEndpoint: Auth start page for Bot.
- TeamsFx__Authentication__ApplicationIdUri: Microsoft Entra app identifies URIs.
Example for TeamsFx Bot template:
Open the
infra/azure.parameters.json
file, add the code toparameters
:"m365ClientId": { "value": "${{AAD_APP_CLIENT_ID}}" }, "m365ClientSecret": { "value": "${{SECRET_AAD_APP_CLIENT_SECRET}}" }, "m365TenantId": { "value": "${{AAD_APP_TENANT_ID}}" }, "m365OauthAuthorityHost": { "value": "${{AAD_APP_OAUTH_AUTHORITY_HOST}}" }
Open the
infra/azure.bicep
file and find the code:param location string = resourceGroup().location
Update the code as:
param m365ClientId string param m365TenantId string param m365OauthAuthorityHost string param m365ApplicationIdUri string = 'api://botid-${botAadAppClientId}' @secure() param m365ClientSecret string
Add the code before output:
resource webAppSettings 'Microsoft.Web/sites/config@2021-02-01' = { name: '${webAppName}/appsettings' properties: { TeamsFx__Authentication__ClientId: m365ClientId TeamsFx__Authentication__ClientSecret: m365ClientSecret TeamsFx__Authentication__Bot__InitiateLoginEndpoint: uri('https://${webApp.properties.defaultHostName}', 'bot-auth-start') TeamsFx__Authentication__OAuthAuthority: uri(m365OauthAuthorityHost, m365TenantId) TeamsFx__Authentication__ApplicationIdUri: m365ApplicationIdUri BOT_ID: botAadAppClientId BOT_PASSWORD: botAadAppClientSecret RUNNING_ON_AZURE: '1' } }
Note
If you want add additional configs to your Azure Webapp, add the configs in the webAppSettings.
Update the
appsettings.json
file and theappsettings.Development.json
file for Microsoft Entra related configs that needs to be configured to your .NET project settings:TeamsFx: { Authentication: { ClientId: AAD app client id ClientSecret: AAD app client secret, OAuthAuthority: AAD app oauth authority, ApplicationIdUri: AAD app identify uri, Bot: { InitiateLoginEndpoint: Auth start page for Bot } } }
Note
You can use
$ENV_NAME$
to reference envs in local/remote service.Example for TeamsFx Bot template:
Open
appsettings.json
andappsettings.Development.json
files, and add the code:"TeamsFx": { "Authentication": { "ClientId": "$clientId$", "ClientSecret": "$client-secret$", "OAuthAuthority": "$oauthAuthority$", "ApplicationIdUri": "$applicationIdUri$", "Bot": { "InitiateLoginEndpoint": "$initiateLoginEndpoint$" } } }
Update your code to add SSO to your Teams app.
You can find samples code:
- TeamsFx SDK: https://www.nuget.org/packages/Microsoft.TeamsFx/
- Sample Code: under
TeamsFx-Auth/Bot
Example for TeamsFx Bot template:
- Open
Config.cs
and replace the code:
using Microsoft.TeamsFx.Configuration; namespace {{YOUR_NAMESPACE}} { public class TeamsFxOptions { public AuthenticationOptions Authentication { get; set; } } public class ConfigOptions { public string BOT_ID { get; set; } public string BOT_PASSWORD { get; set; } public TeamsFxOptions TeamsFx { get; set; } } }
Note
Replace the
{{YOUR_NAMESPACE}}
property with your namespace name.Move
TeamsFx-Auth/Bot/SSO
andTeamsFx-Auth/Bot/Pages
files to/
.Note
Remember to replace
{{YOUR_NAMESPACE}}
with your project namespace.Open the
Program.cs
file, and find the code:
builder.Services.AddSingleton<BotFrameworkAuthentication, ConfigurationBotFrameworkAuthentication>();
Update the code as:
builder.Services.AddRazorPages(); // Create the Bot Framework Adapter with error handling enabled. builder.Services.AddSingleton<IBotFrameworkHttpAdapter, AdapterWithErrorHandler>(); builder.Services.AddSingleton<IStorage, MemoryStorage>(); // Create the Conversation state. (Used by the Dialog system itself.) builder.Services.AddSingleton<ConversationState>(); // The Dialog that will be run by the bot. builder.Services.AddSingleton<SsoDialog>(); // Create the bot as a transient. In this case the ASP Controller is expecting an IBot. builder.Services.AddTransient<IBot, TeamsSsoBot<SsoDialog>>(); builder.Services.AddOptions<BotAuthenticationOptions>().Configure(options => { options.ClientId = config.TeamsFx.Authentication.ClientId; options.ClientSecret = config.TeamsFx.Authentication.ClientSecret; options.OAuthAuthority = config.TeamsFx.Authentication.OAuthAuthority; options.ApplicationIdUri = config.TeamsFx.Authentication.ApplicationIdUri; options.InitiateLoginEndpoint = config.TeamsFx.Authentication.Bot.InitiateLoginEndpoint; });
Find the code:
builder.Services.AddSingleton<HelloWorldCommandHandler>(); builder.Services.AddSingleton(sp => { var options = new ConversationOptions() { Adapter = sp.GetService<CloudAdapter>(), Command = new CommandOptions() { Commands = new List<ITeamsCommandHandler> { sp.GetService<HelloWorldCommandHandler>() } } }; return new ConversationBot(options); });
Update the code as:
builder.Services.AddSingleton(sp => { var options = new ConversationOptions() { Adapter = sp.GetService<CloudAdapter>(), Command = new CommandOptions() { Commands = new List<ITeamsCommandHandler> { } } }; return new ConversationBot(options); });
Find and delete the code:
// Create the bot as a transient. In this case the ASP Controller is expecting an IBot. builder.Services.AddTransient<IBot, TeamsBot>();
Find the code:
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
Update the code as:
app.UseEndpoints(endpoints => { endpoints.MapControllers(); endpoints.MapRazorPages(); });
Note
You need to exclude the sample code under
TeamsFx-Auth
to avoid build failure by adding the following code to.csproj
file:<ItemGroup> <Compile Remove="TeamsFx-Auth/**/*" /> <None Include="TeamsFx-Auth/**/*" /> <Content Remove="TeamsFx-Auth/Tab/GetUserProfile.razor"/> </ItemGroup>
To check if SSO app works as expected, run the
Local Debug
in Visual Studio.You can also run the app in cloud by selecting
Provision in the cloud
and then selectDeploy to the cloud
to update your app.