Authenticate a user in a Microsoft Teams bot

Important

This article is based on the v3 Bot Framework SDK. If you are looking for current documentation version 4.6 or later of the SDK, see the conversational bots section.

There are many services that you may want to consume inside your Teams app, and most of those services require authentication and authorization to get access. The services include Facebook, Twitter, and Teams. Users in Teams have user profile information stored in Azure Active Directory using Microsoft Graph. This topic focuses on authentication using Azure AD to get access. OAuth 2.0 is an open standard for authentication used by Azure AD and many other service providers. Understanding OAuth 2.0 is a prerequisite for working with authentication in Teams and Azure AD. The following examples use the OAuth 2.0 Implicit Grant flow to eventually read the user's profile information from Azure AD and Microsoft Graph.

The authentication flow described in this topic is similar to the tabs, except that tabs can use web-based authentication flow, and bots require authentication to be driven from code. The concepts in this topic will also be useful when implementing authentication from the mobile platform.

For a general overview of authentication flow for bots, see the topic Authentication flow in bots.

Configuring identity providers

See the topic Configure identity providers for detailed steps on configuring OAuth 2.0 callback redirect URL(s) when using Azure Active Directory as an identity provider.

Initiate authentication flow

Authentication flow should be triggered by a user action. Don't open the authentication pop-up automatically, as it might trigger the browser's pop-up blocker and confuse the user.

Add UI to start authentication

Add UI to the bot to enable the user to sign in when needed. Here it's done from a Thumbnail card, in TypeScript:

// Show prompt of options
protected async promptForAction(session: builder.Session): Promise<void> {
    let msg = new builder.Message(session)
        .addAttachment(new builder.ThumbnailCard(session)
            .title(this.providerDisplayName)
            .buttons([
                 builder.CardAction.messageBack(session, "{}", "Sign in")
                     .text("SignIn")
                     .displayText("Sign in"),
                  builder.CardAction.messageBack(session, "{}", "Show profile")
                     .text("ShowProfile")
                     .displayText("Show profile"),
                  builder.CardAction.messageBack(session, "{}", "Sign out")
                     .text("SignOut")
                     .displayText("Sign out"),
            ]));
    session.send(msg);
}

Three buttons have been added to the Hero Card: Sign in, Show Profile, and Sign out.

Sign the user in

Because of the validation that must be performed for security reasons and the support for the mobile versions of Teams, the code isn't shown here, but here's an example of the code that kicks off the process when the user presses the Sign in button..

The validation and mobile support are explained in the topic Authentication flow in bots.

Be sure to add the domain of your authentication redirect URL to the validDomains section of the manifest. If you don't sign in, the pop-up won't appear.

Showing user profile information

Although getting an access token is difficult because of all the transitions back and forth across different websites and the security issues that must be addressed, once you have a token, obtaining information from Azure Active Directory is straightforward. The bot makes a call to the me Graph endpoint with the access token. Graph responds with the user information for the person who logged in. Information from the response is used to construct a bot card and sent.

// Show user profile
protected async showUserProfile(session: builder.Session): Promise<void> {
    let azureADApi = this.authProvider as AzureADv1Provider;
    let userToken = this.getUserToken(session);

    if (userToken) {
        let profile = await azureADApi.getProfileAsync(userToken.accessToken);
        let profileCard = new builder.ThumbnailCard()
            .title(profile.displayName)
            .subtitle(profile.mail)
            .text(`${profile.jobTitle}<br/> ${profile.officeLocation}`);
        session.send(new builder.Message().addAttachment(profileCard));
    } else {
        session.send("Please sign in to AzureAD so I can access your profile.");
    }

    await this.promptForAction(session);
}

// Helper function to make the Graph API call
public async getProfileAsync(accessToken: string): Promise<any> {
    let options = {
        url: "https://graph.microsoft.com/v1.0/me",
        json: true,
        headers: {
            "Authorization": `Bearer ${accessToken}`,
        },
    };
    return await request.get(options);
}

If the user isn't signed in, they're prompted to do so.

Sign the user out

// Handle user logout request
private async handleLogout(session: builder.Session): Promise<void> {
    if (!utils.getUserToken(session, this.providerName)) {
        session.send(`You're already signed out of ${this.providerDisplayName}.`);
    } else {
        utils.setUserToken(session, this.providerName, null);
        session.send(`You're now signed out of ${this.providerDisplayName}.`);
    }

    await this.promptForAction(session);
}

Other samples

For sample code showing the bot authentication process, see: