Sign-In experiences

This blog post discusses the options for creating various sign-on experiences when using WebChat. Sign-on experiences enable the bot to identify the users who are part of a conversation and to perform actions on users’ behalf against identity providers. For example, a bot can ask a user to sign-in to Office 365 so that the bot can check the user’s recent email or check the user’s calendar.

When using the Bot Framework, there are currently two options for driving a sign-in:

  1. Sharing the client’s user token directly with the bot via ChannelData
  2. Using an OAuthCard to drive a sign-in experience to any OAuth provider

A third option, called Single Sign-On (SSO), is in development and is where the client UI takes the client’s user token for the client app and exchanges it for a different token that can be used with the same identity provider, but a different app/scopes. For now, it is possible to create a similar user experience using WebChat by using technique #1 above.

Deciding on an Experience

Deciding which sign-in option to use depends on several factors, including which channel you are using, which application the bot needs the user to sign-in to, and what kind of user experience is desired.

You can use the below decision guide to help select the approach that is best for your situation.

If you are using:

  • WebChat in an unauthenticated website and you want the bot to connect to any identity provider -> use an OAuthCard to sign-in
  • WebChat in an authenticated website where the user is already signed in and
    • the website has a user token to the same identity provider and app that the bot needs -> share the client’s user token in ChannelData
    • the website has a user token to the same identity provider but a different app that the bot needs -> in the future, this is single sign-on, but for now you’ll need to use an OAuthCard
    • the website has a user token to a different identity provider that the bot needs -> use an OAuthCard to sign-in
  • WebChat in an authenticated website where the user has not yet signed in and
    • the bot wants a token to the same identity provider as the website would use -> send an EventActivity to the client to prompt the user to first log-in to the website, then continue with the above cases
    • the bot wants a token to a different identity provider -> use an OAuthCard to sign-in
  • a Microsoft channel (Teams, Cortana, Skype, or Skype for Business) and
    • the bot wants an AAD token -> in the future, this is single sign-on, but for now you’ll need to use an OAuthCard
    • the bot wants a non-AAD token -> use an OAuthCard to sign-in
  • a non-Microsoft channel (Facebook, Slack, Telegram, etc.) -> use an OAuthCard to sign-in

 

How to share the client’s user token in ChannelData

Many websites support authentication so that users can sign in and have the website perform actions on their behalf such as to list order history or to take actions. Once a user has signed in to a website, the user token is typically stored in a cookie and/or in-memory in the user’s web browser. When the user wants to perform an action in his or her web browser on the website, this user token is most often included in the Authorization header to WebAPI calls that the browser makes to backend services. It is a best practice to use https when making these WebAPI calls and to verify the token authenticity on the backend service.

A similar approach applies to WebChat and bots using the Bot Framework in that messages sent from WebChat to the Bot can include the client’s user token in each message. WebChat sends messages using https to the Azure Bot Service, which then signs them and passes them to the bot.

If you want your bot to use the same user token as the client website, then WebChat can be set up to send the user token with every message to the bot. This is preferable to sending the token once and having the bot cache the token because (1) the token could expire, and the bot cannot refresh it and (2) it takes a decent amount of work to securely cache and store user tokens.

To configure WebChat to send user tokens with each outgoing message, you can use the BackChannel capability in WebChat. What this enables you to do is to intercept every outgoing Activity and augment it with any extra information you’d like, including the user token.

To do this, you need to import classes from the botframework-directlinejs into your javascript client:

import { Chat } from 'botframework-webchat';
import { DirectLine, Activity } from 'botframework-directlinejs';

Next, you’ll need to declare your own instance of the DirectLine client and then override the postActivity function so that you can augment it with your own data:

var base = new DirectLine({
secret: this._token,
domain: this._domain,
});
 
var directLine: any = {...base};
 
directLine.postActivity = (activity: Activity) => base.postActivity({
...activity,
channelData: { userToken: this.getUserToken() }
});

The above example calls a method, getUserToken, which gathers the client’s user token (from in-memory or a cookie). Finally, you can use this directLine instance when instantiating the WebChat control, such as in a React application:

<Chat
botConnection={directLine}
/>

When users interact with the WebChat control and send outgoing messages, you can see that the channelData field includes a userToken:

How to trigger a client site sign-in

In some cases, websites are setup to offer an anonymous experience as well as a signed in experience. For example, a banking website might have a chat window that anonymous users can interact with to get basic information such as hours, the bank routing number, or branch address information. That same bot can perform other tasks such as balance inquiries or transfer money, but these would require the user to be signed in.

The bot could send an OAuthCard to ask the user to sign-in. However, if the banking website also includes the ability to sign-in, the user could be confused what exactly they are signing in to. In these cases, it is often preferable to have the bot ask the website to prompt the user to sign in, and then have the resulting user token flow to the bot using channelData. While the above section describes how to send a user token using channelData, the bot needs a way to signal to the website that it needs the user token. This is where you can use a custom EventActivity from the bot to WebChat as the signal.

When the bot receives a message from the user to perform a task that can only be fulfilled if the user is logged in, the bot can send two replies: one as a MessageActivity asking the user to sign in, and a second as an EventActivity with a name such as ‘signinrequest’. WebChat can be configured to listen for events (or any other activity) and can then perform special processing when it receives this event. In this case, the special processing will be to prompt the user to sign in to the website using website controls. Once the user has signed in, the token can be returned to the bot as another EventActivity and in subsequent MessageActivities.

To configure WebChat to intercept incoming activities to ‘listen’ for the ‘signinrequest’ event, you can create your own instance of DirectLine and subscribe to the activity$ observable on the DirectLine object:

var directLine = new DirectLine({ 
      secret: this._token,
      domain: this._domain,
    });

    directLine.activity$.subscribe((activity: Activity) => {
      if (activity.type === 'event' && activity.name === 'signinrequest') {
        // if we receive a signinrequest event from the bot, 
        // prompt the user to sign in
        this.signin().then((token: string) => {
          // once the user is signed in, send the token back to the bot
          directLine.postActivity({
            type: 'event',
            name: 'userToken',
            value: token
          });
        });
      }
    });

 

Note that You can then use this DirectLine instance to initialize a WebChat control, such as in a React application:

<Chat
    botConnection={directLine}
/>

How to use OAuthCards

A tutorial on using OAuthCards can be found here. There are samples for the v3 SDK here for C# and here for Node.

Happy Making!
The Azure Bot Service Team