Use SSO authentication for bots

Single sign-on authentication in Microsoft Azure Active Directory (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. Tabs and bots have similar flow for SSO support. But bot requests tokens and receives responses with a different protocol.

Note

  • OAuth 2.0 is an open standard for authentication and authorization used by Azure AD and many other identity providers. A basic understanding of OAuth 2.0 is a prerequisite for working with authentication in Teams.

  • Bot SSO is supported only in one-on-one chat.

See the following video to learn about single sign-on (SSO) support for bots:


Bot SSO at runtime

The following image illustrates the flow of SSO in bots:

Bot SSO at runtime diagram

The following steps help you with authentication and bot application tokens:

  1. The bot sends a message to Teams with an OAuthCard that contains tokenExchangeResource to obtain an authentication token for the bot application. The user receives messages at all the active user endpoints.

    Note

    • A user can have more than one active endpoint at a time.
    • The bot token is received from every active user endpoint.
    • The app must be installed in personal scope for SSO support.
  2. If the current user is using your bot application for the first time, a request prompt appears to the user to do one of the following actions:

    • Provide consent, if necessary.
    • Handle step-up authentication, such as two-factor authentication.
  3. Teams requests the bot application token from the Azure AD endpoint for the current user.

  4. Azure AD sends the bot application token to the Teams application.

  5. Teams sends the token to the bot as part of the value object returned by the invoking with sign in/tokenExchange.

  6. The parsed token in the bot application provides the required information, such as the user's email address.

Develop an SSO Teams bot

The following steps guide you to develop SSO Teams bot:

  1. Register your app through the Azure AD portal.
  2. Update your Teams application manifest for your bot.
  3. Add the code to request and receive a bot token.

Register your app through the Azure AD portal

The steps to register your app through the Azure AD portal are similar to the tab SSO flow. The following steps guide you to register your app:

  1. Register a new application in the Azure Active Directory – App Registrations portal.

  2. Select New Registration. The Register an application page appears.

    New registration

  3. In the Register an application, do the following steps:

    Note

    The users are not asked for consent and are granted access tokens right away, if the Azure AD app is registered in the same tenant where they are making an authentication request in Teams. However, the users must provide consent to the permissions, if the Azure AD app is registered in a different tenant.

    • Enter Name for your app.
    • Select Supported account types, such as single tenant or multitenant.
    • Select Register.

    Register an application

  4. Go to overview page.

  5. Copy the value of Application (client) ID.

  6. Under Manage, go to Expose an API

    Tip

    To update your app manifest later, save the Application (client) ID value.

    Important

    • If you are building a standalone bot, enter the Application ID URI as api://botid-{YourBotId}. Here YourBotId is your Azure AD application ID.
    • If you are building an app with a bot and a tab, enter the Application ID URI as api://fully-qualified-domain-name.com/botid-{YourBotId}.
  7. Select Add a scope.

  8. In the panel that prompts, enter access_as_user as the Scope name.

    Note

    The "access_as_user" scope used to add a client app is for "Administrators and users".

    You must be aware of the following important restrictions:

    • Only user-level Microsoft Graph API permissions, such as email, profile, offline_access, and OpenId are supported. If you need access to other Microsoft Graph scopes, such as User.Read or Mail.Read, see Extend tab app with Microsoft Graph permissions and scope.
    • Your application's domain name must be same as the domain name that you have registered for your Azure AD application.
    • Multiple domains per app are currently not supported.
    • Applications that use the azurewebsites.net domain are not supported because it is common and may be a security risk.
  9. In the Who can consent?, enter Admins and users.

  10. Enter the following details to configure the admin and user consent prompts with values that are appropriate for the access_as_userscope.

    • Admin consent display name: Teams can access the user’s profile.
    • Admin consent description: Teams can call the app’s web APIs as the current user.
    • User consent display name: Teams can access your profile and make requests on your behalf.
    • User consent description: Teams can call this app’s APIs with the same rights as you have.

    admin and users

  11. Ensure that the state is set to Enabled.

    State

  12. Select Add scope to save the details. The domain part of the Scope name displayed must automatically match the Application ID URI set in the previous step, with /access_as_user appended to the end api://subdomain.example.com/00000000-0000-0000-0000-000000000000/access_as_user.

  13. In the Authorized client applications, identify the applications that you want to authorize for your app’s web application.

  14. Select Add a client application.

    client application

  15. Enter each of the following client IDs and select the authorized scope you created in the previous step:

    • 1fec8e78-bce4-4aaf-ab1b-5451cc387264 for Teams mobile or desktop application.
    • 5e3ce6c0-2b1f-4285-8d4b-75ee78787346 for Teams web application.

    client id

  16. Go to Authentication.

  17. In Platform configurations, select Add a platform.

    platform

  18. Select Web.

    Configure platform

  19. Enter the Redirect URIs for your app.

    Note

    This URI should be a fully qualified domain name. It's also followed by the API route where an authentication response is sent. If you're following any of the Teams samples, the URI is https://token.botframework.com/.auth/web/redirect. For more information, see OAuth 2.0 authorization code flow.

    Redirect uris

  20. The following steps will help you to enable implicit grant:

    • Select Authentication from the left pane.
    • Select the Access tokens and ID tokens checkboxes.

    Grant flow

    • Select Save to save the changes.
  21. Add necessary API Permissions.

    • Select API permissions from the left pane.
    • Select Add a platform to add any permissions that your app requires to downstream APIs, for example, User.Read.

Update manifest in Microsoft Azure portal

The following steps will guide you to update the bot manifest in Azure portal:

  1. Select Manifest from the left pane.

  2. Ensure the config item is set to "accessTokenAcceptedVersion": 2. If not, change it's value to 2.

    Update manifest

    Note

    If you are already in testing your bot in Teams, you must sign out from this app and sign out from Teams. Then sign in again to see this change.

  3. Select Save.

Update the Azure portal with the OAuth connection

The following steps will guide you to update the Azure portal with the OAuth connection:

  1. In the Azure portal, go to AzureBot

  2. Go to Configuration on the left pane.

  3. Select Add OAuth Connection Settings.

    Configuration setting

  4. The following steps will guide you to complete the New Connection Setting form:

    Note

    Implicit grant may be required in the Azure AD application.

    • Enter Name in the New Connection Setting page.

    Note

    The Name is referred to the settings of your bot service code in step 5 of Bot SSO at runtime.

    • From the Service Provider drop-down, select Azure Active Directory v2.
    • Enter the client credentials, such as Client Id and Client secret for the Azure AD application.
    • For the Token Exchange URL, use the scope value defined in Update your Teams application manifest for your bot for example, api://botid-<your-app-id>/. The Token Exchange URL indicates to the SDK that this Azure AD application is configured for SSO.
    • In the Tenant ID, enter common.
    • Add all the Scopes configured when specifying permissions to downstream APIs for your Azure AD application. With the Client ID and Client secret provided, the token store exchanges the token for a graph token with defined permissions.
    • Select Save.
    • Select Apply.

    Connection setting

Update your Teams application manifest for your bot

If the application contains a standalone bot, then use the following code to add new properties to the Teams application manifest:

    "webApplicationInfo": 
        {
            "id": "00000000-0000-0000-0000-000000000000",
            "resource": "api://botid-00000000-0000-0000-0000-000000000000"
        }

If the application contains a bot and a tab, then use the following code to add new properties to the Teams application manifest:

    "webApplicationInfo": 
        {
            "id": "00000000-0000-0000-0000-000000000000",
            "resource": "api://subdomain.example.com/botid-00000000-0000-0000-0000-000000000000"
        }

webApplicationInfo is the parent of the following elements:

  • id - The client ID of the application. It's the application ID that you obtained as part of registering the application with Azure AD. Don't share this Application ID with multiple Teams apps. Create a new Azure AD app for each application manifest that uses webApplicationInfo.
  • resource - The domain and subdomain of your application. It's the same URI, including the api:// protocol that you registered when creating your scope in Register your app through the Azure AD portal. Don't include the access_as_user path in your resource. The domain part of this URI must match the domain and subdomains used in the URLs of your Teams application manifest.

Add the code to request and receive a bot token

Request a bot token

The request to get the token is a normal 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 and it's similar to a sign in card. Teams treats this request as a silent token acquisition 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.

Note

The Microsoft Bot Framework OAuthPrompt or the MultiProviderAuthDialog is supported for SSO authentication.

If the user is using the application for the first time and user consent is required, the following dialog box appears to continue with the consent experience:

Consent dialog box

When the user selects Continue, the following events occur:

  • If the bot defines a sign in button, the sign in flow for bots is activated that is similar to the sign in flow from an OAuth card button in a message stream. The developer must decide which permissions require user's consent. This approach is recommended if you require a token with permissions beyond openId. For example, if you want to exchange the token for graph resources.

  • If the bot isn't providing a sign in button on the OAuth card, user consent is required for a minimal set of permissions. This token is useful for basic authentication and to get the user's email address.

C# token request without a sign in button
    var attachment = new Attachment
            {
                Content = new OAuthCard
                {
                    TokenExchangeResource = new TokenExchangeResource
                    {
                        Id = requestId
                    }
                },
                ContentType = OAuthCard.ContentType,
            };
            var activity = MessageFactory.Attachment(attachment);

            // NOTE: This activity needs to be sent in the 1:1 conversation between the bot and the user. 
            // If the bot supports group and channel scope, this code should be updated to send the request to the 1:1 chat. 

       await turnContext.SendActivityAsync(activity, cancellationToken);

Receive the bot token

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.

Note

You might receive multiple responses for a given request if the user has multiple active endpoints. You must deduplicate the responses with the token.

C# code to handle the invoke activity
    protected override async Task<InvokeResponse> OnInvokeActivityAsync
    (ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
            {
                try
                {
                    if (turnContext.Activity.Name == SignInConstants.TokenExchangeOperationName && turnContext.Activity.ChannelId == Channels.Msteams)
                    {
                        await OnTokenResponseEventAsync(turnContext, cancellationToken);
                        return new InvokeResponse() { Status = 200 };
                    }
                    else
                    {
                        return await base.OnInvokeActivityAsync(turnContext, cancellationToken);
                    }
                }
                catch (InvokeResponseException e)
                {
                    return e.CreateInvokeResponse();
                }
            }

The turnContext.activity.value is of type TokenExchangeInvokeRequest and contains the token that can be further used by your bot. You must store the tokens for performance reasons and refresh them.

Token exchange failure

If there's a token exchange failure, use the following code:

{​​ 
    "status": "<response code>", 
    "body": 
    {​​ 
        "id":"<unique Id>", 
        "connectionName": "<connection Name on the bot (from the OAuth card)>", 
        "failureDetail": "<failure reason if status code is not 200, null otherwise>" 
    }​​ 
}​​

To understand what the bot does when the token exchange fails to trigger a consent prompt, see the following steps:

Note

No user action is required to be taken as the bot takes the actions when the token exchange fails.

  1. The client starts a conversation with the bot triggering an OAuth scenario.

  2. The bot sends back an OAuth card to the client.

  3. The client intercepts the OAuth card before displaying it to the user and checks if it contains a TokenExchangeResource property.

  4. If the property exists, the client sends a TokenExchangeInvokeRequest to the bot. The client must have an exchangeable token for the user, which must be an Azure AD v2 token and whose audience must be the same as TokenExchangeResource.Uri property. The client sends an invoke activity to the bot with the following code:

    {
        "type": "Invoke",
        "name": "signin/tokenExchange",
        "value": 
        {
            "id": "<any unique Id>",
            "connectionName": "<connection Name on the skill bot (from the OAuth card)>",
            "token": "<exchangeable token>"
        }
    }
    
  5. The bot processes the TokenExchangeInvokeRequest and returns a TokenExchangeInvokeResponse back to the client. The client must wait until it receives the TokenExchangeInvokeResponse.

    {
        "status": "<response code>",
        "body": 
        {
            "id":"<unique Id>",
            "connectionName": "<connection Name on the skill bot (from the OAuth card)>",
            "failureDetail": "<failure reason if status code is not 200, null otherwise>"
        }
    }
    
  6. If the TokenExchangeInvokeResponse has a status of 200, then the client doesn't show the OAuth card. See the normal flow image. For any other status or if the TokenExchangeInvokeResponse isn't received, then the client shows the OAuth card to the user. See the fallback flow image. If there are any errors or unmet dependencies like user consent, this activity ensures that the SSO flow falls back to normal OAuthCard flow.

Update the auth sample

Open Teams auth sample, and then complete the following steps to update it:

  1. Update the TeamsBot to handle the deduping of the incoming request by including the following code:

        protected override async Task OnSignInInvokeAsync(ITurnContext<IInvokeActivity> turnContext, CancellationToken cancellationToken)
            {
                await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
            }
        protected override async Task OnTokenResponseEventAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
            {
                await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
            }
    
  2. Update appsettings.json to include the botId, password, and the connection name defined in Update the Azure portal with the OAuth connection.

  3. Update the manifest and ensure that token.botframework.com is in the valid domains list. For more information, see Teams auth sample.

  4. Zip the manifest with the profile images and install it in Teams.

Code sample

Sample name Description .NET C# Node.js
Bot framework SDK This sample code demonstrates how to get started with authentication in a bot for Microsoft Teams. View View View

Step-by-step guide

Follow the step-by-step guide, to build a bot with SSO authentication.