Support for logged-in users
This guide assumes you've set up the chat UI for anonymous/logged-out users. If you haven't, please head over to support for logged-out users first.
With just logged-out support, all your website visitors will need to go through an email-based authentication process in order to verify who they are. This flow captures and verifies their email address, as well as creating a new customer in your Plain workspace.
However, if one of your users is already logged in on your website, you don't want them to go through authentication again. You already know their email address and personal details!
The Chat UI can be configured to help you get your users' details into Plain, which means:
- Your logged-in users can start typing right away when they open the chat.
- They will show up as a customer in your Plain workspace with the correct name and email.
In the logged-in flow:
- The chat UI requests a JWT from your backend. Your backend generates a JWT containing the customer details, signing it with a private key
- The chat UI exchanges the JWT for a session token with Plain
The sequence of this flow is shown here:
There are two prerequisites before we are able to configure the Chat UI to support logged-in users:
- Generating a key pair
- An API to create customer JWTs
1. Generating a key pairโ
This assumes that you are working on macOS. If you are not, please get in touch with us at help@plain.com, and we can provide alternative methods to generate key pairs.
A private/public key pair is required to sign JWTs (on your server) and verify them (on Plains). This is a security measure to prevent bad actors from impersonating your users.
Let's create a key pair and add the public key to your workspace:
- Run the following in your terminal and follow the prompts:
(The source code of this script is here: https://github.com/team-plain/generate-rsa-key-script)
bash <(curl -fsSL https://raw.githubusercontent.com/team-plain/generate-rsa-key-script/main/generate_rsa_key_pairs.sh)
After the script completed it should have created a private key ending in
.key.pem
and a public key ending in.key.pem.pub
.Go to your workspace at app.plain.com and open Settings -> Apps. Choose the one you created earlier. Then click on "Add Public Key"
Choose a name for the public key (this is not shown publicly, so choose something that makes sense to you), paste its contents and click on "Create public key"
After that, you should land back on the workspace app page. It means the public key has been added successfully.
2. Creating customer JWTsโ
A customer JWT is the mechanism by which your users can authenticate as customers on Plain.
Customer JWTs must be created and signed on your servers. Not doing so would mean exposing your private key, so anyone could impersonate your customers while getting in touch with Plain.
The JWT payload must conform to the following schema:
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"type": "object",
"properties": {
"fullName": {
"type": "string",
"description": "The customer full name"
},
"shortName": {
"type": "string",
"description": "The customer first name"
},
"email": {
"type": "object",
"properties": {
"email": {
"type": "string",
"description": "The customer email"
},
"isVerified": {
"type": "boolean",
"description": "Whether or not you have already established that the user has access to the email address they've provided you; for example if they've already verified their email on your app or website."
}
},
"required": [
"email",
"isVerified"
]
},
"externalId": {
"type": "string",
"description": "Your customer's id in your own systems. If provided, this is what is used to identify customers. If Plain receives a customer with the same externalId as one we've seen before, we'll update any details that have changed. If not, we'll create a new customer."
}
},
"required": [
"fullName",
"email",
"externalId"
]
}
How you sign a JWT depends on the programming language you use in your backend.
Here is an example of how you could do this in NodeJS. The code assumes your
private key is exposed as an environment variable PRIVATE_KEY
with no passphrase.
var jwt = require('jsonwebtoken');
const customer = {
fullName: "Garnett Hermann",
shortName: "Garnett",
email: {
email: "garnett.hermann@aol.com",
isVerified: false
},
externalId: "your_id",
};
const token = jwt.sign(customer, process.env.PRIVATE_KEY, {
algorithm: "RS256",
expiresIn: "1h"
});
You can find a similar library for any language on https://jwt.io/libraries.
The way you expose the JWT signing to your website is up to you. You can embed the resulting customer JWT in the page, serve it from an API, etc. It doesn't really matter as long as the JWT is generated server-side and the private key is kept secret.
In this tutorial, we'll assume that we're exposing it through an API
endpoint: POST /api/get-customer-jwt
.
You can see a sample endpoint implementation on our NextJS example app.
Once you have a way to create customer JWTs and expose them, you'll need to configure the chat UI to use the JWT. This can be done either via a script tag or the React npm package.
Script tagโ
If you already set up the script tag for logged-out users, you can update your code to
include the highlighted part below by invoking the set-customer
action.
Alternatively, copy and paste the whole code fragment (replace APP_KEY
with the key you obtained on getting an app key)
<script>
window.$plain = window.$plain || [];
typeof plain === 'undefined' && (plain = function () { $plain.push(arguments); });
plain("init", {
appKey: "APP_KEY"
});
plain('set-customer', {
type: 'logged-in',
getCustomerJwt: () => {
return fetch('/api/get-customer-jwt/', {method: 'POST'})
.then(res => res.json())
.then(res => res.plain_customer_jwt);
}
});
</script>
<script async src="<https://customer-chat.cdn-plain.com/latest/customerChat.js>"></script>
The most important part here is the getCustomerJwt
function. This
function must return a valid customer JWT.
In this case, the function will be called when the page loads, querying
your endpoint /api/get-customer-jwt/
and obtaining the customer JWT
back. This JWT is then sent to Plain and exchanged for a token, which is
then used for the duration of the chat session.
When the user logs out, it is important you also log them out of Plain. You can do this by invoking the following snippet on log out:
plain('set-customer', {
type: 'logged-out'
});
And you are all set. At this point your logged-in users will be able to send chat messages to you, and they will all show up as customers in your workspace in the Support App.
npm packageโ
Did you know we have a sample NextJS app showcasing how to use the React npm package?
With full logged-in user support, including an example on how to use the usePlain
hook to get the
number of unread notifications for a user. Check it out
at team-plain/example-nextjs.
Besides the appKey
prop, the PlainProvider
also takes a prop
called customer
.
The customer
prop can be used to specify the current customer (your
current user), and show them the correct conversations with you.
To specify the logged in customer you have to provide the following
customer
prop to the PlainProvider
(replace APP_KEY
with the key you obtained on getting an app key):
import React from 'react';
import { PlainProvider, Chat } from '@team-plain/react-chat-ui';
const customer = {
type: 'logged-in',
getCustomerJwt: () => {
return fetch('/api/get-customer-jwt', { method: 'POST' })
.then(res => res.json())
.then(res => res.plain_customer_jwt);
}
}
function App() {
return (
<PlainProvider appKey="APP_KEY" customer={customer}>
<h1>Your app</h1>
<Chat/>
</PlainProvider>
);
}
ReactDOM.render(<App/>);
The most important part here is the getCustomerJwt
function. This
function must return a valid customer JWT.
The chat UI will call this function, querying your endpoint
/api/get-customer-jwt/
and obtaining the customer JWT back. This JWT is
then sent to Plain and exchanged for a token, which is then used for the
duration of the chat session.
When the user logs out it is important you also log them out of Plain.
You can do this using the usePlain
hook:
const { logout } = usePlain();
<button onClick={logout}>
Logout
</button>
If you run your app now you should be able to chat in the <Chat/>
component and see customer messages show up in your workspace in the Support App ๐
If you have any problems, please get in touch with us by email on help@plain.com, and we will be happy to help.