diff --git a/msteams-platform/bots/how-to/authentication/add-authentication.md b/msteams-platform/bots/how-to/authentication/add-authentication.md index d4793477efb..b444d9a87bd 100644 --- a/msteams-platform/bots/how-to/authentication/add-authentication.md +++ b/msteams-platform/bots/how-to/authentication/add-authentication.md @@ -21,7 +21,7 @@ For more information about how the Azure Bot Service handles authentication, see In this article you'll learn: - **How to create an authentication-enabled bot**. Use [cs-auth-sample][teams-auth-bot-cs] to handle user sign-in credentials and the generating the authentication token. -- **How to deploy the bot to Azure and associate it with an identity provider**. The provider issues a token based on user sign-in credentials. The bot can use the token to access resources, such as a mail service, which require authentication. For more information, see [Microsoft Teams authentication flow for bots](auth-flow-bot.md). +- **How to deploy the bot to Azure and associate it with an identity provider**. The provider issues a token based on user sign-in credentials. The bot can use the token to access resources, such as a mail service, which require authentication. For more information, see [Microsoft Teams authentication flow for bots](../../../../../../../Downloads/auth-flow-bot.md). - **How to integrate the bot within Microsoft Teams**. Once the bot is integrated, you can sign in and exchange messages with it in a chat. ## Prerequisites @@ -82,7 +82,7 @@ You use a resource group to create individual resources for the Bot Framework. F The Azure Bot resource registration registers your web service as a bot with the Bot Framework, which provides you with a Microsoft App ID and App password (client secret). > [!IMPORTANT] -> You only need to register your bot if it's not hosted in Azure. If you [created a bot](/azure/bot-service/abs-quickstart?view=azure-bot-service-4.0&viewFallbackFrom=azure-bot-service-3.0&preserve-view=true) through the Azure portal then it's already registered with the service. If you created your bot through the [Bot Framework](https://dev.botframework.com/bots/new) or [Developer Portal](../../../concepts/build-and-test/teams-developer-portal.md) your bot isn't registered in Azure. +> You only need to register your bot if it's not hosted in Azure. If you [created a bot](/azure/bot-service/abs-quickstart?view=azure-bot-service-4.0&viewFallbackFrom=azure-bot-service-3.0&preserve-view=true) through the Azure portal then it's already registered with the service. If you created your bot through the [Bot Framework](https://dev.botframework.com/bots/new) or [Developer Portal](../../../../../../../../../concepts/build-and-test/teams-developer-portal.md) your bot isn't registered in Azure. 1. Visit [**Azure portal**][azure-portal] and search for **Azure Bot** in **Create a resource** section. 1. Open the **Azure Bot** and select **Create**. @@ -156,7 +156,7 @@ To add the Microsoft Teams channel: :::image type="content" source="../../../assets/images/adaptive-cards/select-teams.png" alt-text="Screenshot shows how to add Microsoft Teams channel."::: -For more information, see [Create a bot for Teams](../create-a-bot-for-teams.md). +For more information, see [Create a bot for Teams](../../../../../../../create-a-bot-for-teams.md). > [!div class="nextstepaction"] > [I ran into an issue](https://github.com/MicrosoftDocs/msteams-docs/issues/new?template=Doc-Feedback.yaml&title=%5BI+ran+into+an+issue%5D+Create+Azure+Bot+resource+registration&&author=%40surbhigupta&pageUrl=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fmicrosoftteams%2Fplatform%2Fbots%2Fhow-to%2Fauthentication%2Fadd-authentication%3Ftabs%3Ddotnet%252Cdotnet-sample%23create-azure-bot-resource-registration&contentSourceUrl=https%3A%2F%2Fgithub.com%2FMicrosoftDocs%2Fmsteams-docs%2Fblob%2Fmain%2Fmsteams-platform%2Fbots%2Fhow-to%2Fauthentication%2Fadd-authentication.md&documentVersionIndependentId=70952f91-56e9-ff08-59f6-e237d4aaeca9&platformId=cc53b20b-69e0-cb70-1ca7-9b939c969c92&metadata=*%2BID%253A%2Be473e1f3-69f5-bcfa-bcab-54b098b59c80%2B%250A*%2BService%253A%2B%2A%2Amsteams%2A%2A) @@ -207,7 +207,7 @@ You need an identity provider for authentication. In this procedure, you use a M 1. Select **Add OAuth Connection Settings**. The following image displays the corresponding selection in the resource page: - ![SampleAppDemoBot configuration](~/assets/images/authentication/sample-app-demo-bot-configuration.png) + ![SampleAppDemoBot configuration](../../../../../../../Downloads/~/assets/images/authentication/sample-app-demo-bot-configuration.png) 1. Complete the form as follows: @@ -285,7 +285,7 @@ The bot code uses the connection name to retrieve user authentication tokens. With the preliminary settings done, let's focus on the creation of the bot to use in this article. -# [C#/.NET](#tab/dotnet) +# [C#/.NET](../../../../../../../Downloads/add-authentication.md#tab/dotnet) 1. Clone [cs-auth-sample][teams-auth-bot-cs]. 1. Open Visual Studio. @@ -298,14 +298,14 @@ With the preliminary settings done, let's focus on the creation of the bot to us Depending on the characters in your bot secret, you might need to XML escape the password. For example, any ampersands (&) must be encoded as `&`. - [!code-json[appsettings](~/../Microsoft-Teams-Samples/samples/bot-teams-authentication/csharp/appsettings.json?range=1-5)] + [!code-json[appsettings](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-teams-authentication/csharp/appsettings.json)] 1. In the Solution Explorer, go to the `TeamsAppManifest` folder, open `manifest.json` and set `id` and `botId` to the **bot App ID** you saved at the time of the bot registration. For more information, see [app manifest](/microsoft-365/extensibility/schema/root-bots#botid). > [!div class="nextstepaction"] > [I ran into an issue](https://github.com/MicrosoftDocs/msteams-docs/issues/new?template=Doc-Feedback.yaml&title=%5BI+ran+into+an+issue%5D+Prepare+the+bot+sample+code+using+C%23%2F.NET&&author=%40surbhigupta&pageUrl=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fmicrosoftteams%2Fplatform%2Fbots%2Fhow-to%2Fauthentication%2Fadd-authentication%3Ftabs%3Dnode-js%252Cdotnet-sample%23test-the-connection&contentSourceUrl=https%3A%2F%2Fgithub.com%2FMicrosoftDocs%2Fmsteams-docs%2Fblob%2Fmain%2Fmsteams-platform%2Fbots%2Fhow-to%2Fauthentication%2Fadd-authentication.md&documentVersionIndependentId=70952f91-56e9-ff08-59f6-e237d4aaeca9&platformId=cc53b20b-69e0-cb70-1ca7-9b939c969c92&metadata=*%2BID%253A%2Be473e1f3-69f5-bcfa-bcab-54b098b59c80%2B%250A*%2BService%253A%2B%2A%2Amsteams%2A%2A) -# [JavaScript](#tab/node-js) +# [JavaScript](../../../../../../../Downloads/add-authentication.md#tab/node-js) 1. Clone [node-auth-sample][teams-auth-bot-js]. 1. In a console, go to the project:

@@ -319,14 +319,14 @@ With the preliminary settings done, let's focus on the creation of the bot to us - Set the `connectionName` to the name of the identity provider connection. Depending on the characters in your bot secret, you might need to XML escape the password. For example, any ampersands (&) must be encoded as `&`. - [!code-javascript[settings](~/../Microsoft-Teams-Samples/samples/bot-conversation-sso-quickstart/js/.env)] + [!code-javascript[settings](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-conversation-sso-quickstart/js/.env)] 1. In the `teamsAppManifest` folder, open `manifest.json` and set `id` to your **Microsoft App ID** and `botId` to the **bot App ID** you saved at the time of the bot registration. > [!div class="nextstepaction"] > [I ran into an issue](https://github.com/MicrosoftDocs/msteams-docs/issues/new?template=Doc-Feedback.yaml&title=%5BI+ran+into+an+issue%5D+Prepare+the+bot+sample+code+using+JavaScript&&author=%40surbhigupta&pageUrl=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fmicrosoftteams%2Fplatform%2Fbots%2Fhow-to%2Fauthentication%2Fadd-authentication%3Ftabs%3Dnode-js%252Cdotnet-sample%23prepare-the-bot-sample-code&contentSourceUrl=https%3A%2F%2Fgithub.com%2FMicrosoftDocs%2Fmsteams-docs%2Fblob%2Fmain%2Fmsteams-platform%2Fbots%2Fhow-to%2Fauthentication%2Fadd-authentication.md&documentVersionIndependentId=70952f91-56e9-ff08-59f6-e237d4aaeca9&platformId=cc53b20b-69e0-cb70-1ca7-9b939c969c92&metadata=*%2BID%253A%2Be473e1f3-69f5-bcfa-bcab-54b098b59c80%2B%250A*%2BService%253A%2B%2A%2Amsteams%2A%2A) -# [Python](#tab/python) +# [Python](../../../../../../../Downloads/add-authentication.md#tab/python) 1. Clone [py-auth-sample][teams-auth-bot-py] from the GitHub repository. 1. Update **config.py**: @@ -336,7 +336,7 @@ With the preliminary settings done, let's focus on the creation of the bot to us Depending on the characters in your bot secret, you might need to XML escape the password. For example, any ampersands (&) must be encoded as `&`. - [!code-python[config](~/../Microsoft-Teams-Samples/samples/bot-teams-authentication/python/config.py?range=14-16)] + [!code-python[config](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-teams-authentication/python/config.py)] > [!div class="nextstepaction"] > [I ran into an issue](https://github.com/MicrosoftDocs/msteams-docs/issues/new?template=Doc-Feedback.yaml&title=%5BI+ran+into+an+issue%5D+Prepare+the+bot+sample+code+using+Python&&author=%40surbhigupta&pageUrl=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fmicrosoftteams%2Fplatform%2Fbots%2Fhow-to%2Fauthentication%2Fadd-authentication%3Ftabs%3Dpython%252Cdotnet-sample%23prepare-the-bot-sample-code&contentSourceUrl=https%3A%2F%2Fgithub.com%2FMicrosoftDocs%2Fmsteams-docs%2Fblob%2Fmain%2Fmsteams-platform%2Fbots%2Fhow-to%2Fauthentication%2Fadd-authentication.md&documentVersionIndependentId=70952f91-56e9-ff08-59f6-e237d4aaeca9&platformId=cc53b20b-69e0-cb70-1ca7-9b939c969c92&metadata=*%2BID%253A%2Be473e1f3-69f5-bcfa-bcab-54b098b59c80%2B%250A*%2BService%253A%2B%2A%2Amsteams%2A%2A) @@ -591,17 +591,17 @@ With authentication, Teams behaves differently than other channels. An **Invoke Activity** is sent to the bot rather than the Event Activity used by other channels, which is done by subclassing the **ActivityHandler**. -# [C#/.NET](#tab/dotnet-sample) +# [C#/.NET](../../../../../../../Downloads/add-authentication.md#tab/dotnet-sample) **Bots/DialogBot.cs** -[!code-csharp[ActivityHandler](~/../Microsoft-Teams-Samples/samples/bot-teams-authentication/csharp/Bots/DialogBot.cs?range=19-51)] +[!code-csharp[ActivityHandler](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-teams-authentication/csharp/Bots/DialogBot.cs)] **Bots/TeamsBot.cs** The *Invoke Activity* must be forwarded to the dialog if the **OAuthPrompt** is used. -[!code-csharp[ActivityHandler](~/../Microsoft-Teams-Samples/samples/bot-teams-authentication/csharp/Bots/TeamsBot.cs?range=34-42)] +[!code-csharp[ActivityHandler](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-teams-authentication/csharp/Bots/TeamsBot.cs)] #### TeamsActivityHandler.cs @@ -628,17 +628,17 @@ protected virtual Task OnSigninVerifyStateAsync(ITurnContext tu > [!div class="nextstepaction"] > [I ran into an issue](https://github.com/MicrosoftDocs/msteams-docs/issues/new?template=Doc-Feedback.yaml&title=%5BI+ran+into+an+issue%5D+Handling+Invoke+Activity+using+C%23%2F.NET&&author=%40surbhigupta&pageUrl=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fmicrosoftteams%2Fplatform%2Fbots%2Fhow-to%2Fauthentication%2Fadd-authentication%3Ftabs%3Ddotnet%252Cdotnet-sample%23handling-invoke-activity&contentSourceUrl=https%3A%2F%2Fgithub.com%2FMicrosoftDocs%2Fmsteams-docs%2Fblob%2Fmain%2Fmsteams-platform%2Fbots%2Fhow-to%2Fauthentication%2Fadd-authentication.md&documentVersionIndependentId=70952f91-56e9-ff08-59f6-e237d4aaeca9&platformId=cc53b20b-69e0-cb70-1ca7-9b939c969c92&metadata=*%2BID%253A%2Be473e1f3-69f5-bcfa-bcab-54b098b59c80%2B%250A*%2BService%253A%2B%2A%2Amsteams%2A%2A) -# [JavaScript](#tab/node-js-dialog-sample) +# [JavaScript](../../../../../../../Downloads/add-authentication.md#tab/node-js-dialog-sample) **bots/dialogBot.js** -[!code-javascript[ActivityHandler](~/../Microsoft-Teams-Samples/samples/bot-conversation-sso-quickstart/js/bots/dialogBot.js?range=4-46)] +[!code-javascript[ActivityHandler](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-conversation-sso-quickstart/js/bots/dialogBot.js)] **bots/teamsBot.js** The *Invoke Activity* must be forwarded to the dialog if the **OAuthPrompt** is used. -[!code-javascript[ActivityHandler](~/../Microsoft-Teams-Samples/samples/bot-conversation-sso-quickstart/js/bots/teamsBot.js?range=4-33)] +[!code-javascript[ActivityHandler](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-conversation-sso-quickstart/js/bots/teamsBot.js)] **dialogs/mainDialog.js** @@ -647,44 +647,44 @@ Within a dialog step, use `beginDialog` to start the OAuth prompt, which asks th - If the user is already signed in, it generates a token response event, without prompting the user. - Otherwise, it prompts the user to sign in. The Azure Bot Service sends the token response event after the user attempts to sign in. -[!code-javascript[AddOAuthPrompt](~/../Microsoft-Teams-Samples/samples/bot-conversation-sso-quickstart/js/dialogs/mainDialog.js?range=50-52)] +[!code-javascript[AddOAuthPrompt](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-conversation-sso-quickstart/js/dialogs/mainDialog.js)] Within the following dialog step, check for the presence of a token in the result from the previous step. If it isn't null, then the user successfully signed in. -[!code-javascript[AddOAuthPrompt](~/../Microsoft-Teams-Samples/samples/bot-conversation-sso-quickstart/js/dialogs/mainDialog.js?range=50-64)] +[!code-javascript[AddOAuthPrompt](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-conversation-sso-quickstart/js/dialogs/mainDialog.js)] **dialogs/logoutDialog.js** -[!code-javascript[allow-logout](~/../Microsoft-Teams-Samples/samples/bot-conversation-sso-quickstart/js/dialogs/logoutDialog.js?range=31-42&highlight=7)] +[!code-javascript[allow-logout](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-conversation-sso-quickstart/js/dialogs/logoutDialog.js)] > [!div class="nextstepaction"] > [I ran into an issue](https://github.com/MicrosoftDocs/msteams-docs/issues/new?template=Doc-Feedback.yaml&title=%5BI+ran+into+an+issue%5D+Handling+Invoke+Activity+using+JavaScript&&author=%40surbhigupta&pageUrl=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fmicrosoftteams%2Fplatform%2Fbots%2Fhow-to%2Fauthentication%2Fadd-authentication%3Ftabs%3Ddotnet%252Cnode-js-dialog-sample%23handling-invoke-activity&contentSourceUrl=https%3A%2F%2Fgithub.com%2FMicrosoftDocs%2Fmsteams-docs%2Fblob%2Fmain%2Fmsteams-platform%2Fbots%2Fhow-to%2Fauthentication%2Fadd-authentication.md&documentVersionIndependentId=70952f91-56e9-ff08-59f6-e237d4aaeca9&platformId=cc53b20b-69e0-cb70-1ca7-9b939c969c92&metadata=*%2BID%253A%2Be473e1f3-69f5-bcfa-bcab-54b098b59c80%2B%250A*%2BService%253A%2B%2A%2Amsteams%2A%2A) -# [Python](#tab/python-sample) +# [Python](../../../../../../../Downloads/add-authentication.md#tab/python-sample) **bots/dialog_bot.py** -[!code-python[ActivityHandler](~/../Microsoft-Teams-Samples/samples/bot-teams-authentication/python/bots/dialog_bot.py?range=10-42)] +[!code-python[ActivityHandler](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-teams-authentication/python/bots/dialog_bot.py)] **bots/teams_bot.py** The *Invoke Activity* must be forwarded to the dialog if the **OAuthPrompt** is used. -[!code-python[on_token_response_event](~/../Microsoft-Teams-Samples/samples/bot-teams-authentication/python/bots/teams_bot.py?range=38-45)] +[!code-python[on_token_response_event](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-teams-authentication/python/bots/teams_bot.py)] **dialogs/main_dialog.py** Within a dialog step, use `begin_dialog` to start the OAuth prompt, which asks the user to sign in. If the user is already signed in, it generates a token response event, without prompting the user. Otherwise, it prompts the user to sign in. The Azure Bot Service sends the token response event after the user attempts to sign in. -[!code-python[Add OAuthPrompt](~/../Microsoft-Teams-Samples/samples/bot-teams-authentication/python/dialogs/main_dialog.py?range=48-49)] +[!code-python[Add OAuthPrompt](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-teams-authentication/python/dialogs/main_dialog.py)] Within the following dialog step, check for the presence of a token in the result from the previous step. If it isn't null, then the user successfully signed in. -[!code-python[Add OAuthPrompt](~/../Microsoft-Teams-Samples/samples/bot-teams-authentication/python/dialogs/main_dialog.py?range=51-61)] +[!code-python[Add OAuthPrompt](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-teams-authentication/python/dialogs/main_dialog.py)] **dialogs/logout_dialog.py** -[!code-python[allow logout](~/../Microsoft-Teams-Samples/samples/bot-teams-authentication/python/dialogs/logout_dialog.py?range=29-36&highlight=6)] +[!code-python[allow logout](../../../../../../../Downloads/Microsoft-Teams-Samples/samples/bot-teams-authentication/python/dialogs/logout_dialog.py)] > [!div class="nextstepaction"] > [I ran into an issue](https://github.com/MicrosoftDocs/msteams-docs/issues/new?template=Doc-Feedback.yaml&title=%5BI+ran+into+an+issue%5D+Handling+Invoke+Activity+using+Python&&author=%40surbhigupta&pageUrl=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fmicrosoftteams%2Fplatform%2Fbots%2Fhow-to%2Fauthentication%2Fadd-authentication%3Ftabs%3Ddotnet%252Cpython-sample%23handling-invoke-activity&contentSourceUrl=https%3A%2F%2Fgithub.com%2FMicrosoftDocs%2Fmsteams-docs%2Fblob%2Fmain%2Fmsteams-platform%2Fbots%2Fhow-to%2Fauthentication%2Fadd-authentication.md&documentVersionIndependentId=70952f91-56e9-ff08-59f6-e237d4aaeca9&platformId=cc53b20b-69e0-cb70-1ca7-9b939c969c92&metadata=*%2BID%253A%2Be473e1f3-69f5-bcfa-bcab-54b098b59c80%2B%250A*%2BService%253A%2B%2A%2Amsteams%2A%2A) diff --git a/msteams-platform/bots/how-to/authentication/bot-sso-code.md b/msteams-platform/bots/how-to/authentication/bot-sso-code.md index 89e11703761..62702e3ff1e 100644 --- a/msteams-platform/bots/how-to/authentication/bot-sso-code.md +++ b/msteams-platform/bots/how-to/authentication/bot-sso-code.md @@ -4,7 +4,7 @@ description: Learn how to add code configuration, handle an access token, receiv ms.topic: how-to ms.localizationpriority: high zone_pivot_groups: enable-sso -ms.date: 11/13/2024 +ms.date: 03/20/2026 ms.owner: ryanbliss --- # Add code to enable SSO in your bot app @@ -24,9 +24,9 @@ You need to configure your app's code to obtain an access token from Microsoft E This section covers: 1. [Update development environment variables](#update-development-environment-variables) -1. [Add code to handle an access token](#add-code-to-handle-an-access-token) -1. [Add code to receive the token](#add-code-to-receive-the-token) -1. [Handle app user sign out](#handle-app-user-sign-out) +1. [Configure OAuth and error handling](#configure-oauth-and-error-handling) +1. [Handle authentication and sign-in](#handle-authentication-and-sign-in) +1. [Handle sign out](#handle-sign-out) ## Update development environment variables @@ -38,10 +38,10 @@ To update the development environment variables: 1. Open the environment file for your project. 1. Update the following variables: - - For `MicrosoftAppId`, update the bot ID from Microsoft Entra ID. - - For `MicrosoftAppPassword`, update the client secret. + - For `ClientId`, update the bot ID from Microsoft Entra ID. + - For `ClientSecret`, update the client secret. - For `ConnectionName`, update the name of the OAuth connection you configured in Microsoft Entra ID. - - For `MicrosoftAppTenantId`, update the tenant ID. + - For `TenantId`, update the tenant ID. > [!NOTE] > You can customize the OAuth redirect URL for your bot and identity provider based on your data residency requirements, irrespective of whether your bot is in the public cloud, Microsoft Azure Government cloud, or Microsoft Azure operated by 21Vianet. For OAuth URLs and data residency list, see [OAuth URL support in Azure AI Bot Service](/azure/bot-service/ref-oauth-redirect-urls?view=azure-bot-service-4.0&preserve-view=true). @@ -53,193 +53,80 @@ You've now configured the required environment variables for your bot app and SS > [!div class="nextstepaction"] > [I ran into an issue](https://github.com/MicrosoftDocs/msteams-docs/issues/new?template=Doc-Feedback.yaml&title=%5BI+ran+into+an+issue%5D+Update+development+environment+variables&&author=%40surbhigupta&pageUrl=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fmicrosoftteams%2Fplatform%2Fbots%2Fhow-to%2Fauthentication%2Fbot-sso-code%3Ftabs%3Dcs1%252Ccs2%252Ccs3%252Ccs4%252Ccs5%26pivots%3Dbot-app%23update-development-environment-variables&contentSourceUrl=https%3A%2F%2Fgithub.com%2FMicrosoftDocs%2Fmsteams-docs%2Fblob%2Fmain%2Fmsteams-platform%2Fbots%2Fhow-to%2Fauthentication%2Fbot-sso-code.md&documentVersionIndependentId=039ff5cc-7243-ce4b-527e-c152755eeb72&platformId=915789b2-9617-01bb-fb21-d6789a634ed8&metadata=*%2BID%253A%2Be473e1f3-69f5-bcfa-bcab-54b098b59c80%2B%250A*%2BService%253A%2B%2A%2Amsteams%2A%2A) -## Add code to handle an access token +## Configure OAuth and error handling -The request to get the token is a POST message request using the existing message schema. It's included in the attachments of an OAuthCard. The schema for the OAuthCard class is defined in [Microsoft Bot Schema 4.0](/dotnet/api/microsoft.bot.schema.oauthcard?view=botbuilder-dotnet-stable&preserve-view=true). Teams refreshes the token if the `TokenExchangeResource` property is populated on the card. For the Teams channel, only the `Id` property, which uniquely identifies a token request, is honored. +Teams SDK simplifies authentication setup with built-in OAuth support and error handling. ->[!NOTE] -> The Microsoft Bot Framework `OAuthPrompt` or the `MultiProviderAuthDialog` is supported for SSO authentication. +# [C#](#tab/cs1) -To update your app's code: +```csharp +var builder = WebApplication.CreateBuilder(args); +var connectionName = builder.Configuration["Teams:ConnectionName"]; -1. Add code snippet for `TeamsSSOTokenExchangeMiddleware`. +// Configure Teams app with OAuth builder.AddTeams(App.Builder().AddOAuth(connectionName)); - # [C#](#tab/cs1) +var app = builder.Build(); +var teams = app.UseTeams(); - Add the following code snippet to `AdapterWithErrorHandler.cs` (or the equivalent class in your app's code): +var logger = app.Services.GetRequiredService() + .CreateLogger("BotAuthQuickstart"); - ```csharp - base.Use(new TeamsSSOTokenExchangeMiddleware(storage, configuration["ConnectionName"])); - ``` +// Handle error events teams.OnError(async (_, @event) => { logger.LogError(@event.Exception, "Error occurred"); }); - # [JavaScript](#tab/js1) +app.Run(); +``` - Add the following code snippet to `index.js` (or the equivalent class in your app's code): +# [JavaScript](#tab/js1) - ```JavaScript - const {TeamsSSOTokenExchangeMiddleware} = require('botbuilder'); - const tokenExchangeMiddleware = new TeamsSSOTokenExchangeMiddleware(memoryStorage, env.connectionName); - adapter.use(tokenExchangeMiddleware); - ``` +```JavaScript +import { App } from "@microsoft/teams.apps"; + +const app = new App({ + oauth: { + defaultConnectionName: process.env.CONNECTION_NAME || 'graph' + } +}); + +// Teams SDK handles errors automatically +// Optional: Add custom error logging in message handlers +app.on('message', async (context) => { + try { + // Your bot logic + } catch (error) { + context.logger?.error(`Error: ${error}`); + await context.send("An error occurred."); + } +}); +``` - --- +# [Python](#tab/py1) - > [!NOTE] - > You might receive multiple responses for a given request if the user has multiple active endpoints. You must eliminate all duplicate or redundant responses with the token. For more information about signin/tokenExchange, see [TeamsSSOTokenExchangeMiddleware Class](/python/api/botbuilder-core/botbuilder.core.teams.teams_sso_token_exchange_middleware.teamsssotokenexchangemiddleware?view=botbuilder-py-latest&preserve-view=true#remarks). - -1. Use the following code snippet for requesting a token. - - # [C#](#tab/cs2) - - After you add the `AdapterWithErrorHandler.cs`, the following code must appear: - - ```csharp - public class AdapterWithErrorHandler : CloudAdapter - { - public AdapterWithErrorHandler( - IConfiguration configuration, - IHttpClientFactory httpClientFactory, - ILogger logger, - IStorage storage, - ConversationState conversationState) - : base(configuration, httpClientFactory, logger) - { - base.Use(new TeamsSSOTokenExchangeMiddleware(storage, configuration["ConnectionName"])); - - OnTurnError = async (turnContext, exception) => - { - // Log any leaked exception from the application. - // NOTE: In production environment, you must consider logging this to - // Azure Application Insights. Visit https://learn.microsoft.com/azure/bot-service/bot-builder-telemetry?view=azure-bot-service-4.0&tabs=csharp to see how - // to add telemetry capture to your bot. - logger.LogError(exception, $"[OnTurnError] unhandled error : {exception.Message}"); - - // Send a message to the user. - await turnContext.SendActivityAsync("The bot encountered an error or bug."); - await turnContext.SendActivityAsync("To continue to run this bot, please fix the bot source code."); - - if (conversationState != null) - { - try - { - // Delete the conversationState for the current conversation to prevent the - // bot from getting stuck in an error-loop caused by being in a bad state. - // conversationState must be thought of as similar to "cookie-state" in a Web pages. - await conversationState.DeleteAsync(turnContext); - } - catch (Exception e) - { - logger.LogError(e, $"Exception caught on attempting to Delete ConversationState : {e.Message}"); - } - } - - // Send a trace activity, which is displayed in the Bot Framework Emulator. - await turnContext.TraceActivityAsync( - "OnTurnError Trace", - exception.Message, - "https://www.botframework.com/schemas/error", - "TurnError"); - }; - } - } - ``` - - # [JavaScript](#tab/js2) - - After you add the code snippet for `TeamsSSOTokenExchangeMiddleware`, the following code must appear: - - ```JavaScript - // index.js is used to setup and configure your bot. - - // Import required packages - const path = require('path'); - - // Read botFilePath and botFileSecret from .env file. - const ENV_FILE = path.join(__dirname, '.env'); - require('dotenv').config({ path: ENV_FILE }); - - const express = require("express"); - - // Import required bot services. - // See https://learn.microsoft.com/azure/bot-service/bot-builder-basics?view=azure-bot-service-4.0 to learn more about the different parts of a bot. - const { - CloudAdapter, - ConversationState, - MemoryStorage, - UserState, - ConfigurationBotFrameworkAuthentication, - TeamsSSOTokenExchangeMiddleware - } = require('botbuilder'); - - const { TeamsBot } = require('./bots/teamsBot'); - const { MainDialog } = require('./dialogs/mainDialog'); - const { env } = require('process'); - - const botFrameworkAuthentication = new ConfigurationBotFrameworkAuthentication(process.env); - - var conname = env.connectionName; - - console.log(`\n${ conname } is the con name`); - - // Create adapter. - // See https://learn.microsoft.com/javascript/api/botbuilder-core/botadapter?view=botbuilder-ts-latest to learn more about how bot adapter. - const adapter = new CloudAdapter(botFrameworkAuthentication); - const memoryStorage = new MemoryStorage(); - const tokenExchangeMiddleware = new TeamsSSOTokenExchangeMiddleware(memoryStorage, env.connectionName); - - adapter.use(tokenExchangeMiddleware); - adapter.onTurnError = async (context, error) => { - // This check writes out errors to console log .vs. app insights. - // NOTE: In production environment, you must consider logging this to Azure - // application insights. See https://learn.microsoft.com/azure/bot-service/bot-builder-telemetry?view=azure-bot-service-4.0&tabs=csharp for telemetry - // configuration instructions. - console.error(`\n [onTurnError] unhandled error: ${ error }`); - - // Send a trace activity, which is displayed in Bot Framework Emulator. - await context.sendTraceActivity( - 'OnTurnError Trace', - `${ error }`, - 'https://www.botframework.com/schemas/error', - 'TurnError' - ); - - // Send a message to the user. - await context.sendActivity('The bot encountered an error or bug.'); - await context.sendActivity('To continue to run this bot, please fix the bot source code.'); - // Clear out state. - await conversationState.delete(context); - }; - - // Define the state store for your bot. - // See https://aka.ms/about-bot-state to learn more about using MemoryStorage. - // A bot requires a state storage system to persist the dialog and user state between messages. - //const memoryStorage = new MemoryStorage(); - - // Create conversation and user state with in-memory storage provider. - const conversationState = new ConversationState(memoryStorage); - const userState = new UserState(memoryStorage); - - // Create the main dialog. - const dialog = new MainDialog(); - // Create the bot that will handle incoming messages. - const bot = new TeamsBot(conversationState, userState, dialog); - - // Create express application. - const expressApp = express(); - expressApp.use(express.json()); - - const server = expressApp.listen(process.env.port || process.env.PORT || 3978, () => { - console.log(`\n${ expressApp.name } listening to`, server.address()); - console.log('\nGet Bot Framework Emulator: https://aka.ms/botframework-emulator'); - console.log('\nTo talk to your bot, open the emulator select "Open Bot"'); - }); - - // Listen for incoming requests. - expressApp.post('/api/messages', async (req, res) => { - // Route received a request to adapter for processing. - await adapter.process(req, res, (context) => bot.run(context)); - }); - ``` +```Python +import os +import logging + +from microsoft_teams.apps import App, AppOptions, ErrorEvent + + +logger = logging.getLogger(__name__) + + +# Configure Teams app with OAuth +app_options = AppOptions( + default_connection_name=os.getenv("CONNECTION_NAME", "graph") +) +app = App(**app_options) + + +# Handle error events +@app.event("error") +async def handle_error_event(event: ErrorEvent): + """Handle error events.""" + logger.error(f"Error occurred: {event.error}") + + if event.context: + logger.error(f"Context: {event.context}") +--- > [!div class="nextstepaction"] > [I ran into an issue](https://github.com/MicrosoftDocs/msteams-docs/issues/new?template=Doc-Feedback.yaml&title=%5BI+ran+into+an+issue%5D+Add+code+to+handle+an+access+token&&author=%40surbhigupta&pageUrl=https%3A%2F%2Flearn.microsoft.com%2Fen-us%2Fmicrosoftteams%2Fplatform%2Fbots%2Fhow-to%2Fauthentication%2Fbot-sso-code%3Ftabs%3Dcs1%252Ccs2%252Ccs3%252Ccs4%252Ccs5%26pivots%3Dbot-app%23add-code-to-handle-an-access-token&contentSourceUrl=https%3A%2F%2Fgithub.com%2FMicrosoftDocs%2Fmsteams-docs%2Fblob%2Fmain%2Fmsteams-platform%2Fbots%2Fhow-to%2Fauthentication%2Fbot-sso-code.md&documentVersionIndependentId=039ff5cc-7243-ce4b-527e-c152755eeb72&platformId=915789b2-9617-01bb-fb21-d6789a634ed8&metadata=*%2BID%253A%2Be473e1f3-69f5-bcfa-bcab-54b098b59c80%2B%250A*%2BService%253A%2B%2A%2Amsteams%2A%2A) @@ -322,111 +209,180 @@ If the user permissions are granted by default or for trusted apps, the user who If you encounter any errors, see [Troubleshoot SSO authentication in Teams](../../../tabs/how-to/authentication/tab-sso-troubleshooting.md). -## Add code to receive the token +## Handle authentication and sign-in -The response with the token is sent through an invoke activity with the same schema as other invoke activities that the bots receive today. The only difference is the invoke name, sign in/tokenExchange, and the **value** field. The **value** field contains the **Id**, a string of the initial request to get the token and the **token** field, a string value including the token. +Teams SDK simplifies authentication with automatic token handling. No manual token management or dialogs are needed. -Use the following code snippet to invoke response: +### Check authentication status and trigger sign-in + +# [C#](#tab/cs2) + +```csharp +// Helper function to handle authentication +async Task<Microsoft.Graph.GraphServiceClient?> GetAuthenticatedGraphClient(IContext context) +{ + if (!context.IsSignedIn) + { + await context.Send("🔐 Please sign in first to access Microsoft Graph."); + await context.SignIn(); + return null; + } + + try + { + return context.GetUserGraphClient(); + } + catch (Exception ex) + { + logger.LogError(ex, "Failed to create Graph client"); + await context.Send("🔐 Failed to create authenticated client. Trying to sign in again."); + await context.SignIn(); + return null; + } +} + +// Handle sign-in command +async Task HandleSignInCommand(IContext context) +{ + if (context.IsSignedIn) + { + await context.Send("✅ You are already signed in!"); + } + else + { + await context.Send("🔐 Signing you in to access Microsoft Graph..."); + await context.SignIn(); + } +} + +// Register command handler +teams.OnMessage("signin", async context => await HandleSignInCommand(context)); +``` + +# [JavaScript](#tab/js2) + +```JavaScript +// Helper function to handle authentication +async function getAuthenticatedGraphClient(context: any): Promise<any | null> { + if (!context.isSignedIn) { + await context.send("🔐 Please sign in first to access Microsoft Graph."); + await context.signin(); + return null; + } + + try { + return context.userGraph; + } catch (error: any) { + context.logger?.error(`Failed to create Graph client: ${error}`); + await context.send("🔐 Failed to create authenticated client. Trying to sign in again."); + await context.signin(); + return null; + } +} + +// Handler for signin command +async function handleSignin(context: any): Promise<void> { + if (context.isSignedIn) { + await context.send("✅ You are already signed in!"); + } else { + await context.send("🔐 Signing you in to access Microsoft Graph..."); + await context.signin(); + } +} + +// Dispatch incoming messages +app.on('message', async (context) => { + const text = context.activity.text?.toLowerCase().trim() || ''; + + if (text === 'signin') { + await handleSignin(context); + } +}); +``` + +# [Python](#tab/py2) + +```Python +# Helper function to handle authentication +async def get_authenticated_graph_client(ctx: ActivityContext[MessageActivity]): + """ + Helper function to handle authentication and create Graph client using Token pattern. + """ + if not ctx.is_signed_in: + await ctx.send("🔐 Please sign in first to access Microsoft Graph.") + await ctx.sign_in() + return None + + try: + return get_graph_client(ctx.user_token) + + except Exception as e: + ctx.logger.error(f"Failed to create Graph client: {e}") + await ctx.send("🔐 Failed to create authenticated client. Trying to sign in again.") + await ctx.sign_in() + return None + + +# Handle sign-in command +async def handle_signin_command(ctx: ActivityContext[MessageActivity]): + """Handle sign-in command.""" + if ctx.is_signed_in: + await ctx.send("✅ You are already signed in!") + else: + await ctx.send("🔐 Signing you in to access Microsoft Graph...") + await ctx.sign_in() + + +# Register command handler +@app.on_message_pattern("signin") +async def signin_handler(ctx: ActivityContext[MessageActivity]): + await handle_signin_command(ctx) + +--- + +### Handle successful sign-in events # [C#](#tab/cs3) ```csharp -public MainDialog(IConfiguration configuration, ILogger logger) - : base(nameof(MainDialog), configuration["ConnectionName"]) - { - AddDialog(new OAuthPrompt( - nameof(OAuthPrompt), - new OAuthPromptSettings - { - ConnectionName = ConnectionName, - Text = "Please Sign In", - Title = "Sign In", - Timeout = 300000, // User has 5 minutes to login (1000 * 60 * 5) - EndOnInvalidMessage = true - })); - - AddDialog(new ConfirmPrompt(nameof(ConfirmPrompt))); - - AddDialog(new WaterfallDialog(nameof(WaterfallDialog), new WaterfallStep[] - { - PromptStepAsync, - LoginStepAsync, - })); - - // The initial child Dialog to run. - InitialDialogId = nameof(WaterfallDialog); - } - - -private async Task PromptStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) - { - return await stepContext.BeginDialogAsync(nameof(OAuthPrompt), null, cancellationToken); - } - -private async Task LoginStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken) - { - - var tokenResponse = (TokenResponse)stepContext.Result; - if (tokenResponse?.Token != null) - { - var token = tokenResponse.Token; - - // On successful login, the token contains sign in token. - } - else - { - await stepContext.Context.SendActivityAsync(MessageFactory.Text("Login was not successful please try again."), cancellationToken); - } - - return await stepContext.EndDialogAsync(cancellationToken: cancellationToken); - } +teams.OnSignIn(async (_, @event) => +{ + var context = @event.Context; + + await context.Send( + "✅ **Successfully signed in!**\n\n" + + "You can now use these commands:\n\n" + + "â€ĸ profile - View your profile\n\n" + + "â€ĸ signout - Sign out when done" + ); +}); ``` # [JavaScript](#tab/js3) ```JavaScript -class MainDialog { - - this.addDialog(new OAuthPrompt(OAUTH_PROMPT, { - connectionName: process.env.connectionName, - text: 'Please Sign In', - title: 'Sign In', - timeout: 300000 - })); - - this.addDialog(new ConfirmPrompt(CONFIRM_PROMPT)); - - this.addDialog(new WaterfallDialog(MAIN_WATERFALL_DIALOG, [ - this.promptStep.bind(this), - this.loginStep.bind(this), - ])); - - this.initialDialogId = MAIN_WATERFALL_DIALOG; - - } - -async promptStep(stepContext) { - try { - return await stepContext.beginDialog(OAUTH_PROMPT); - } catch (err) { - console.error(err); - } - } - -async loginStep(stepContext) { - // Get the token from the previous step. Note that we could also have gotten the - // token directly from the prompt itself. There is an example of this in the next method. - const tokenResponse = stepContext.result; - if (!tokenResponse || !tokenResponse.token) { - await stepContext.context.sendActivity('Login was not successful please try again.'); - } else { - const token = tokenResponse.token; - // On successful login, the token contains sign in token. - } - return await stepContext.endDialog(); - } +app.event('signin', async (context) => { + await context.send( + "✅ **Successfully signed in!**\n\n" + + "You can now use these commands:\n\n" + + "â€ĸ profile - View your profile\n\n" + + "â€ĸ signout - Sign out when done" + ); +}); ``` +# [Python](#tab/py3) + +```Python +@app.event("sign_in") +async def handle_sign_in_event(event: SignInEvent): + """Handle successful sign-in events.""" + await event.activity_ctx.send( + "✅ **Successfully signed in!**\n\n" + "You can now use these commands:\n\n" + "â€ĸ **profile** - View your profile\n\n" + "â€ĸ **signout** - Sign out when done" + ) --- > [!NOTE] @@ -440,7 +396,7 @@ async loginStep(stepContext) { Web APIs on your server must decode the access token and verify if it's sent from the client. > [!NOTE] -> If you use Bot Framework, it handles the access token validation. If you don't use Bot Framework, follow the guidelines given in this section. +> If you use Teams SDK, it handles the access token validation. If you don't use Teams SDK, follow the guidelines given in this section. For more information about validating access token, see [Validate tokens](/azure/active-directory/develop/access-tokens#validate-tokens). @@ -481,60 +437,69 @@ The following code snippet is a typical decoded payload of an access token: } ``` -## Handle app user sign out +## Handle sign out -Use the following code snippet to handle the access token in case the app user signs out: +Teams SDK provides a simple sign-out method to clear the user's authentication session. # [C#](#tab/cs4) ```csharp - private async Task InterruptAsync(DialogContext innerDc, - CancellationToken cancellationToken = default(CancellationToken)) - { - if (innerDc.Context.Activity.Type == ActivityTypes.Message) - { - var text = innerDc.Context.Activity.Text.ToLowerInvariant(); - - // Allow logout anywhere in the command. - if (text.IndexOf("logout") >= 0) - { - // The UserTokenClient encapsulates the authentication processes. - var userTokenClient = innerDc.Context.TurnState.Get(); - await userTokenClient.SignOutUserAsync( - innerDc.Context.Activity.From.Id, - ConnectionName, - innerDc.Context.Activity.ChannelId, - cancellationToken - ).ConfigureAwait(false); - - await innerDc.Context.SendActivityAsync(MessageFactory.Text("You have been signed out."), cancellationToken); - return await innerDc.CancelAllDialogsAsync(cancellationToken); - } - } - - return null; - } +async Task HandleSignOutCommand(IContext context) +{ + if (!context.IsSignedIn) + { + await context.Send("â„šī¸ You are not currently signed in."); + } + else + { + await context.SignOut(); + await context.Send("👋 You have been signed out successfully!"); + } +} + +// Register command handler +teams.OnMessage("signout", async context => await HandleSignOutCommand(context)); ``` # [JavaScript](#tab/js4) ```JavaScript - async interrupt(innerDc) { - if (innerDc.context.activity.type === ActivityTypes.Message) { - const text = innerDc.context.activity.text.toLowerCase(); - if (text === 'logout') { - const userTokenClient = innerDc.context.turnState.get(innerDc.context.adapter.UserTokenClientKey); - - const { activity } = innerDc.context; - await userTokenClient.signOutUser(activity.from.id, this.connectionName, activity.channelId); - - await innerDc.context.sendActivity('You have been signed out.'); - return await innerDc.cancelAllDialogs(); - } - } - } +async function handleSignout(context: any): Promise<void> { + if (!context.isSignedIn) { + await context.send("â„šī¸ You are not currently signed in."); + } else { + await context.signout(); + await context.send("👋 You have been signed out successfully!"); + } +} + +// Dispatch incoming messages +app.on('message', async (context) => { + const text = context.activity.text?.toLowerCase().trim() || ''; + + if (text === 'signout' || text === 'logout') { + await handleSignout(context); + } +}); ``` +# [Python](#tab/py4) + +```Python +async def handle_signout_command(ctx: ActivityContext[MessageActivity]): + """Handle sign-out command.""" + if not ctx.is_signed_in: + await ctx.send("â„šī¸ You are not currently signed in.") + else: + await ctx.sign_out() + await ctx.send("👋 You have been signed out successfully!") + + +# Register command handler +@app.on_message_pattern("signout") +async def signout_handler(ctx: ActivityContext[MessageActivity]): + await handle_signout_command(ctx) + --- > [!div class="nextstepaction"] @@ -544,7 +509,7 @@ Use the following code snippet to handle the access token in case the app user s | **Sample name** | **Description** | **C#** | **Node.js** | | --- | --- | --- | --- | -| Bot conversation SSO quick start | Quickly set up Teams bot with SSO for seamless user authentication for one-on-one and group chats. | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-conversation-sso-quickstart/csharp_dotnetcore/BotConversationSsoQuickstart) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/bot-conversation-sso-quickstart/js) | +| Bot Auth Quickstart | This sample demonstrates how to implement Single Sign-On (SSO) authentication for Microsoft Teams bots using Azure Active Directory | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-auth-quickstart/dotnet/bot-auth-quickstart) | [View](https://github.com/OfficeDev/Microsoft-Teams-Samples/tree/main/samples/TeamsSDK/bot-auth-quickstart/nodejs/bot-auth-quickstart) | ::: zone-end