Kindly Chat Authentication

Kindly supports authenticated your users. When your users are authenticated, it's easier to identify them in the Kindly Inbox and you can create more personalized and powerful webhook integrations.

To set this up you need to do a few things:

  • Add a callback to Kindly Chat initialization code.

  • Create a private/public key pair.

  • Create an authentication endpoint that returns a JWT in a specific format.

After the inital implementation and setup you are able to:

  • See user details in the Kindly Inbox.

  • Identify users in webhooks (via the forwarded JWT).

Add a callback to Kindly Chat

The chat client embeded on your site needs to know where your authentication endpoint is, it is responsible for calling that endpoint and feeding the token to Kindly. You need to define a callback function on window.kindlySettings.getAuthToken, like the example below, which must return the JWT token. The function is called with a single argument chatId which identifies the current chat. If you are lucky enough to have the token available in memory already, the callback could be made even simpler.

Example initialization code with callback using the browser fetch API:

<script type="text/javascript">
window.kindlyOptions = {
getAuthToken: async function (chatId) {
return fetch('/your-auth-endpoint', {
method: 'POST',
body: JSON.stringify({ chatId }),
})
.then(response => response.json())
.then(({ token }) => token)
}
}
</script>
<script id="kindly-chat" src="https://chat.kindlycdn.com/kindly-chat.js" data-bot-key="YOUR-BOT-KEY" async></script>

Create a private/public key pair

To allow your own server to authenticate webhook requests without the Kindly API interfering, we use a standard private/public key pair.

The private key is used by your server to encode and sign the JWT in your authentication endpoint.

Add your public key to your bot under Settings -> Chat bubble -> Authentication. The public key is used by the API to verify that the JWT is valid.

Run the following commands to generate a key pair.

openssl genrsa -out private.pem 2048
openssl rsa -in private.pem -outform PEM -pubout -out public.pem

The contents of private.pem should look something like:

-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAsuFRo2/65cN2VLsqJYdedGfWnivezQoWUT0z7qhZcDHbq2iv
+8L4AhkephZ1LZS2/FEJkrTMCtPrR/JCRvBjgF+shbW0HnSAmovsvApy6k1w6qUl
...
-----END RSA PRIVATE KEY-----

and the contents of public.pem should look like this:

-----BEGIN PUBLIC KEY-----
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsuFRo2/65cN2VLsqJYde
dGfWnivezQoWUT0z7qhZcDHbq2iv+8L4AhkephZ1LZS2/FEJkrTMCtPrR/JCRvBj
...
FQIDAQAB
-----END PUBLIC KEY-----

Create an authentication endpoint

The responsibility of authenticating your users is yours and is done by an HTTP endpoint you implement. In the most common case, the Kindly Chat callback sends a HTTP POST request to an authentication endpoint you implement. If the Kindly Chat JavaScript request is sent from the same domain as the endpoint and CORS headers can be omited.

Create an authentication endpoint that returns a JWT in the format specified below. Be aware that JWTs are encoded and signed, not encrypted, meaning anyone who has the token can read the content.

Set up an authentication endpoint on your backend that returns a JSON Web Token (JWT) signed using the RS256 algorithm and the private key created in the step above, and the payload must conform to the following required format:

You can implement the auth endpoint however you wish. We recommend using a compatible JWT library from the list at https://jwt.io/.

Token format

claim

Required

Description

iat

Yes

Integer timestamp (epoch time) when the token is generated.

exp

Yes

Integer timestamp (epoch time) when the token is going to expire. Maximum 15 minutes after iat (being generated).

iss

Yes

String representing the issuer of this token.

sub

Yes

String representing your id for the logged in user.

name

String value representing the full name of the user. Displayed in Kindly Chat and Inbox.

email

String value representing the email of the user. Displayed in Kindly Chat and Inbox.

email_verified

Boolean value indicating whether the email is verified. Only verified emails are stored.

phone_number

String value representing the phone number of the user. Displayed in Kindly Chat and Inbox.

phone_number_verified

Boolean value indicating whether the phone number is verified. Only verified phone numbers are stored.

picture

An URL, which is publicly accessible, pointing to a user avatar. Displayed in Kindly Chat and Inbox.

chat

A JSON object containing the listed keys. Example: { id: "abc123", webhook_domains: ["example.com", "*.example.org"] }

chat.id

String value provided by Kindly Chat, representing the current chat.

chat.webhook_domains

Array of domains. A domain whitelist that Kindly is allowed to forward the token to.

You may also include any other property you want to use in your webhooks. You can read more on JWT in the standard here.

Python example using Django REST framework

import datetime
import jwt
from django.conf import settings
from django.http.response import HttpResponseBadRequest, JsonResponse
from django.utils import timezone
from rest_framework.decorators import api_view, permission_classes
from rest_framework.permissions import IsAuthenticated
@api_view(['POST'])
@permission_classes((IsAuthenticated,))
def auth(request):
chat_id = request.data.get('chat_id')
if not chat_id:
return HttpResponseBadRequest()
now = timezone.now()
iat = int(now.timestamp())
expires = int((now + datetime.timedelta(minutes=1)).timestamp()) # JWT is valid for 1 minute
user = request.user
payload = {
'iat': iat,
'exp': expires,
'chat': {'id': chat_id},
'sub': str(user.id),
'name': user.get_full_name(),
'email': user.email,
'email_verified': True,
'picture': 'https://example.com/avatar.jpg',
}
token = jwt.encode(payload, settings.CHAT_CLIENT_AUTH_PRIVATE_KEY, algorithm='RS256').decode('utf-8')
return JsonResponse({'token': token}, status=200)

Node.js example using Micro web framework

import fs from "fs";
import path from "path";
import micro, { json } from "micro";
import jwt from "jsonwebtoken";
const privKeyPath = path.join(__dirname, "private.pem");
const privKey = fs.readFileSync(privKeyPath);
export default micro(async (req, res) => {
const { chat_id: chatId } = await json(req);
if (!chatId) {
throw new Error("Missing POST parameter chat_id");
}
const token = jwt.sign(
{
sub: "123abc",
name: "John Doe",
picture: "https://example.com/avatar.jpg",
email_verified: true,
chat: {
id: chatId,
webhook_domains: ["example.com", "*.example.org"],
},
},
privKey,
{
algorithm: "RS256",
expiresIn: "5m",
}
);
return { token };
});

Identify users in webhooks

Once the authentication setup is done, a JWT token from your authentication endpoint is passed to the Kindly API which can send the token to a webhook you specify (using advanced replies). When a webhook contains a token it can be used to identify a user and provide support for a wide range of integration use cases.

You'll find the JWT token in the standard Authorization header, ie. Authorization: Bearer <JWT>. Remember, you can use the debug console to inspect webhook request and responses, including their headers.

Webhook domains

To prevent leaking tokens to unwanted third parties, the webhook domains a JWT token can be forwared to must be encoded in the token. Thus the tokens will not automatically be sent from Kindly to all webhooks in a bot. This also implies that the domains must be known in advance. See the section about token format for more info.

Verify JWT token using your public key

To be able to act on behalf of the user in a webhook request, you first need to verify the JWT token, using the public key you generated earlier. After you have done that, you can extracting information from it and be sure of it's authenticity.

Note that Kindly also verifies the JWT using the the public key you provide to us. The public key can be found under Settings -> Chat bubble -> Authentication for your bot.