End user authentication

Some features within the healthcare agent service are available when the end-user is authenticated. How an end-user is authenticated depends on the channel and are discussed in this documentation article.

Features that require end user authentication

Due to security and privacy requirements, the following features require end-user authentication to be enabled:

  • Forget me: When end users are authenticated, different information can be stored and associated with the end user, when end-users want to be forgotten they can trigger the built-in language model to forget them.
  • View personal data: When end users are authenticated, end users can request what the bot knows about them.
  • User Consent: When end users are authenticated, they can be prompted to explicitly consent to terms when they begin a new interaction with your bot.

Enable end user authentication

Enabling end user authentication isn't a strict requirement to communicate with the healthcare agent service, but it's required for the features described in the previous article. To enable end user authentication, navigate to Configuration >> Compliance >> Security >> End user authentication. Here you can enable end user authentication.

A screenshot of the end user authentication

Important

Currently end user authentication is only available in Webchat or via Directline. Other channels will be enabled in the future.

Webchat and Directline

To secure the communications with the healthcare agent service instance, the host application should retrieve in advance two secrets, the app_secret and the webchat_secret. Secrets can be found in the WebChat channel configuration page.

A screenshot of the webchat channel

Steps to securing Webchat or Directline communications

A diagram of the webchat flow

  1. Before the chat with the bot begins, the partner's service should create a security JWT token.

  2. The partner's service should acquire connectorToken---a token used to connect with the Microsoft Bot Framework Connector (also known as Bot Connector). This token is provisioned as described in the Microsoft article Bot Framework Authentication. The webchat secret is required.

  3. The application service should prepare a security JWT token that is returned as a response to the chat request. The JWT security token is signed with the app_secret, and should contain the following elements:

    • userID: A unique, consistent, and unidentifiable user identifier that is used by the healthcare agent service to retrieve data about the user for better personalization. The tenant is expected to obfuscate the user ID in a way that only the tenant is able to restore.

    Important

    the tenant should make sure to generate a different user ID for different end-users.

    • connectorToken: as described in the previous section.
    • optionalAttributes: This is a JSON structure that describes optional application attributes that may be used in the healthcare agent service scenarios---, for example, user location. An example of how to create the JWT token is provided in this healthcare agent service Container Sample GitHub repository.
  4. The application client side should start a web chat against the Bot Connector. This request should include the previously acquired connectorToken.

  5. Right after the chat connection is initiated the application client side should post an "InitAuthenticatedConversation" event that contains the previously acquired jwtToken. An example of the client-side code is provided in this healthcare agent service Container Sample GitHub repository.

Note: The secrets should be stored in a secure way on the partner application side and not exposed to the client side.

Example Code

In this snippet, you can see how to generate a token via an Azure Function. The userId should be a unique userID coming from your backends.

using System;
using System.IO;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.Http;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Security.Claims;
using TokenGenerator;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;

namespace TokenGenerator
{
    public static class TokenGenerator
    {
        [FunctionName("GenereratorFunction")]
        public static async Task<IActionResult> Run(
            [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)] HttpRequest req,
            ILogger log)
        {
            using (HttpClient client = new HttpClient())
            {
                client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", Environment.GetEnvironmentVariable("WEBCHAT_SECRET"));
                client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                HttpResponseMessage response = await client.PostAsync("https://directline.botframework.com/v3/directline/tokens/generate", null);

                var token = GenerateToken(JsonConvert.DeserializeObject<DirectLinePayload>(await response.Content.ReadAsStringAsync()));
                return new OkObjectResult(token);
            }
        }

        private static string GenerateToken(DirectLinePayload payload, int expireMinutes = 200)
        {
            var symmetricKey = Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("APP_SECRET"));
            var tokenHandler = new JwtSecurityTokenHandler();

            var now = DateTime.UtcNow;
            var claims = new ClaimsIdentity(new[]
                {
                    new Claim("userId", "your unique userid"),
                    new Claim("userName", "you"),
                    new Claim("connectorToken", payload.token)
                });
            var tokenDescriptor = new SecurityTokenDescriptor
            {
                Subject = claims,
                Expires = now.AddMinutes(Convert.ToInt32(expireMinutes)),
                SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(symmetricKey), SecurityAlgorithms.HmacSha256Signature)
            };

            var stoken = tokenHandler.CreateToken(tokenDescriptor);
            var token = tokenHandler.WriteToken(stoken);

            return token;
        }
    }
    
    public class DirectLinePayload
    {
        public string conversationId { get; set; }
        public string token { get; set; }
        public int expires_in { get; set; }
    }
}

For the client side integration of the webchat, you can find more information here. Directline API information can be found here

Next steps

Setting up your Webchat