Add single sign-on

Completed

In this unit, you learn how to add single sign-on to a message extension using Microsoft Entra.

Configure Teams app with single sign-on

The Microsoft Teams app supports single sign-on (SSO) using Microsoft Entra. SSO allows users to sign in once and access multiple applications without having to sign in again.

To configure SSO, you configure the Azure Bot resource Microsoft Entra app registration to make requests on behalf of the user.

Screenshot of the Expose API page in the Microsoft Entra app registration.

On the Microsoft Entra app registration, you configure the following settings:

  • Application ID URI, a unique identifier for the app registration that is used to identify the resource that the client should use when initiating a single sign-on flow in the app.
  • Scopes, the permission granted to your bot to access user data.
  • Authorized client applications, the client applications that are allowed to request tokens from the app registration.

In the app manifest, the webApplicationInfo object is configured with the ID of the bot and the Application ID URI. They determine the resource that the client should use when initiating a single sign-on flow in the app.

The following code snippet shows how the webApplicationInfo object is configured in the app manifest file:

"webApplicationInfo": {
    "id": "dbeb8ea7-8ac3-4bb2-bcba-b210b087a73a",
    "resource": "api://grttxrk0-5130.uks1.devtunnels.ms/botid-dbeb8ea7-8ac3-4bb2-bcba-b210b087a73a"
}

Configure OAuth connection

The Azure Bot resource provides access to the Bot Framework Token Service. The token service is used to obtain an access token for the user using the OAuth connection settings that you configure. Tokens are stored securely in the service and are retrieved when needed in the bot code.

Screenshot of an OAuth connection setting in the Azure portal.

To create a connection setting, you configure the following settings:

  • Name - the name of the connection setting.
  • Service provider - the identity provider that the connection uses to authenticate users. Connection settings support several identity providers, including Microsoft Entra.
  • Client ID - the client ID from a Microsoft Entra app registration configured with the required permissions to access user data.
  • Client secret - the client secret from a Microsoft Entra app registration configured with the required permissions to access user data.
  • Token Exchange URL - the URL that the client should use when initiating a single sign-on flow in the app.
  • Tenant ID - the tenant ID where the Microsoft Entra app registration is located.
  • Scopes - the permission granted to your bot to access user data.

The following code snippet shows how to configure an OAuth connection setting in a Bicep file:

resource botServiceProductsAPIConnection 'Microsoft.BotService/botServices/connections@2022-09-15' = {
    parent: botService
    name: 'Products API'
    location: 'global'
    properties: {
        serviceProviderDisplayName: 'Azure Active Directory v2'
        serviceProviderId: '30dd229c-58e3-4a48-bdfd-91ec48eb906c'
        clientId: productsApiEntraAppClientId
        clientSecret: productsApiEntraAppClientSecret
        scopes: 'api://${backendApiEntraAppClientId}/Product.Read'
        parameters: [
            {
                key: 'tenantID'
                value: 'common'
            }
            {
                key: 'tokenExchangeUrl'
                value: 'api://${botAppDomain}/botid-${botEntraAppClientId}'
            }
        ]
    }
}

Tip

Creating new Microsoft Entra app registrations for user to service authentication is recommended. The app registration used with the Azure Bot service should remain separate and used for service to service authentication between the web service and the bot service.

The Microsoft Entra app registration used with the connection setting must be configured to use the redirect URI https://token.botframework.com/.auth/web/redirect to successfully obtain an access token from the Bot Framework Token Service. The redirect URI is used to return the access token to the token service after the user authenticates.

As the token service redirect URI is hosted on an external domain, it must be included in the app manifest file. The validDomains array tells the client which domains are to be used by the app. It ensures that the client can trust the domain when initiating a single sign-on flow.

The following code snippet shows how to add the domain to the app manifest file:

"validDomains": [
    "token.botframework.com"
]

Authenticate user queries

To authenticate user queries in a message extension, you use the Bot Framework SDK to obtain an access token for the user from the Bot Framework Token Service. The access token can then be used to access data from an external service.

First you check if a token exists for the user in the token service.

The following code snippet shows how to get the UserTokenClient and use the GetUserTokenAsync method to return an access token for the user using a named OAuth connection setting:

var userTokenClient = turnContext.TurnState.Get<UserTokenClient>();

var tokenResponse = await userTokenClient.GetUserTokenAsync(userId, connectionName, channelId, magicCode, cancellationToken);

If a token isn't returned, you need to ask the user to sign-in. Create a response that renders a link in the user interface, which the user can follow to start the sign-in flow.

The following code snippet shows how the GetSignInResourceAsync method is used to return a sign-in link from the token service and include it in the message extension response:

var resource = await userTokenClient.GetSignInResourceAsync(connectionName, activity, null, cancellationToken);

return new MessagingExtensionResponse
{
    ComposeExtension = new MessagingExtensionResult
    {
        Type = "auth",
        SuggestedActions = new MessagingExtensionSuggestedAction
        {
            Actions = [
                new() {
                    Type = ActionTypes.OpenUrl,
                    Value = resource.SignInLink,
                    Title = "Sign In",
                },
            ],
        },
    },
};

When the user follows the sign-in link, they consent to the permissions requested in the Scopes field on the OAuth connection setting. After the user consents to the permissions, the token service obtains an access token and stores it securely in the service.

The next time the user initiates a search in the message extension, an access token will be found in the token service. The returned access token can be used to access data from an external service.