Add user authentication
Add user authentication and authorization using JWT Tokens
Sometimes, you may want to authenticate a user when calling the search and chat API. Typical scenarios include:
- Restrict answers of the bot to content that a user has access to. Typically applicable for products with enterprise tiers or private documentation.
- Provide context about the user or apply filters to scope a conversation to a known context in a way that cannot be tampered.
The Inkeep search and chat API provides the ability to authenticate users by providing a User-Token
header that contains a JWT token identifying the user.
Example headers:
{
"Authorization": "Bearer <YOUR_INKEEP_INTEGRATION_API_KEY>"
"User-Token": "<JWT_TOKEN_SIGNED_BY_YOUR_BACKEND>"
}
Configuring the Integration
Generate a private and public key pair using openssl
or the method of your choice.
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:2048
openssl rsa -pubout -in private_key.pem -out public_key.pem
The private key will be used by your backend service to mint tokens that are passed to the Inkeep API. It should not be shared with any other service or party.
In your Inkeep Integration, configure the following:
- Require user authentication: set to
true
- Public key: provide the value of
public_key.pem
- Issuer: the value we should ensure matches the
iss
claim in the authentication token. Example:https://auth.mydomain.com
. This is just a string URI, it can be arbitrary, it just needs to uniquely identify your service.
We will use the public key to validate the token that is provided to our service was created (‘minted’) by your service. We check the signature of the token and the iss
, exp
, aud
and other standard claims.
Token Claims
The token should include the following claims:
iss
: The issuer of the token. Examplehttps://auth.mydomain.com
.aud
: The audience of the token. It should always behttps://api.inkeep.com
.integrationId
: The ID of the integration. This must match the integration ID in the body of the request.filters
: Same as documented in the (Start new chat)[/inkeep-api/create-new-chat-session#chatfiltersinput] request body. Takes precedence over thefilters
value provided in the request body.userAttributes
: Same as documented in the (Start new chat)[/inkeep-api/create-new-chat-session#newsessionchatresultinput] request body. Takes precedence over theuserAttributes
value provided in the request body.exp
: The expiration time of the token, in seconds since the epoch.
The Authorization
: Bearer {integrationApiKey}
header must still be present in the request.
Generating a Token
Here is an example of how to generate a token in Node.js:
import fs from "fs";
import jwt from "jsonwebtoken";
interface TokenConfiguration {
iss: string;
aud: string;
integrationId: string;
filters: {
attributes: {
$and: Array<{
[key: string]: string | { $in: string[] };
}>;
};
};
userAttributes?: {
[key: string]: string | number | string[];
};
expirationOffsetHours?: number;
}
const generateToken = ({
iss,
aud,
integrationId,
filters,
userAttributes,
expirationOffsetHours = 24,
}: TokenConfiguration): string => {
const privateKey = fs.readFileSync("private_key.pem", "utf8"); // your private key
const payload = {
iss,
aud: "https://api.inkeep.com",
integrationId,
filters,
userAttributes,
exp: Math.floor(Date.now() / 1000) + expirationOffsetHours * 60 * 60,
};
const token = jwt.sign(payload, privateKey, { algorithm: "RS256" });
return token;
};
const exampleConfiguration: TokenConfiguration = {
iss: "https://auth.mydomain.com",
integrationId: "inkeep-integration-id",
filters: {
attributes: {
$and: [
{
env: "dev",
},
{
modules: {
$in: ["module1", "module2"],
},
},
],
},
},
userAttributes: {
userId: "123456789",
userEmail: "me@inkeep.com",
userCohorts: [`group1`, `group2`],
},
expirationOffsetHours: 24,
};
const token = generateToken(exampleConfiguration);