Authentication

By default, everyone who uses the VableAI widget on your site is anonymous. To tell VableAI who a signed-in user is, create an access token on your server with your own login system and pass it to the SDK.

The token is a JSON Web Token (JWT). You sign it with a private key that stays on your server, and VableAI checks it with the matching public key that you upload.

Access tokens work with the React and Vue SDKs. The script tag and Flutter installs don't support them yet.

What a token does

FeatureWith a valid token
GreetingThe chat input greets the user by their first name
Conversation recordsThe user's ID, email and name are saved with new conversations

A token doesn't change anything else:

  • Anonymous visitors can still use the widget. A token identifies a user, but VableAI doesn't require one.
  • The AI agent doesn't see the token or the user's details. To give the agent context about the user, use useIntentState.
  • VableAI doesn't call your APIs with the token. Actions run in the user's browser through intents, so they use the session your app already has.

How it works

sequenceDiagram
  participant App as Your frontend
  participant Server as Your server
  participant Widget as VableAI widget
  participant Vable as VableAI
  App->>Server: Request a token, using your own session
  Server->>Server: Check the session and sign a JWT with your private key
  Server-->>App: Access token
  App->>Widget: Pass the token to the SDK
  Widget->>Vable: Start a conversation with the token
  Vable->>Vable: Check the signature with your public key
  Vable-->>Widget: Conversation started

1. Create a signing key

Generate an RSA key pair:

openssl genrsa -out vable_private.pem 2048
openssl rsa -in vable_private.pem -pubout -out vable_public.pem

Keep vable_private.pem in your server's secret storage. Never ship it to the browser or commit it to source control.

2. Upload the public key

  1. Open the VableAI dashboard and go to Settings > API.
  2. Under Signing Key, select Set Signing Key.
  3. Paste the full contents of vable_public.pem, including the -----BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- lines.

Each project has one signing key. If you replace it, tokens signed with the old key stop working straight away, so deploy the new private key at the same time.

The signing key isn't the same as the publicKey you pass to the SDK. publicKey identifies your VableAI project, and the signing key verifies your tokens.

3. Sign a token on your server

Add an endpoint that only signed-in users can reach. Sign the token with the RS256 algorithm.

ClaimRequiredDescription
subYesYour unique ID for the user
expRecommendedWhen the token expires. Tokens without it never expire
firstNameNoUsed to greet the user
lastNameNoSaved with the conversation
emailNoSaved with the conversation

This example uses Node.js with Express and the jsonwebtoken package:

import jwt from "jsonwebtoken";

const privateKey = process.env.VABLE_SIGNING_PRIVATE_KEY;

app.get("/api/vable-token", requireLogin, (req, res) => {
  const token = jwt.sign(
    {
      email: req.user.email,
      firstName: req.user.firstName,
      lastName: req.user.lastName,
    },
    privateKey,
    {
      algorithm: "RS256",
      subject: String(req.user.id),
      expiresIn: "24h",
    },
  );

  res.json({ token });
});

Anyone with the token can read what's inside it, so only include the claims listed above.

4. Pass the token to the SDK

Fetch the token after the user signs in, then pass it to the Vable component as accessToken. The widget works anonymously until the token arrives.

React

import { useEffect, useState } from "react";
import { Vable } from "@vableai/react/init";

function App() {
  const [accessToken, setAccessToken] = useState<string>();

  useEffect(() => {
    fetch("/api/vable-token", { credentials: "include" })
      .then((response) => (response.ok ? response.json() : null))
      .then((data) => setAccessToken(data?.token));
  }, []);

  return (
    <Vable
      publicKey="<replace_with_public_key>"
      accessToken={accessToken}
      routes={routes}
    >
      {/* your app */}
    </Vable>
  );
}

Vue

<template>
  <Vable
    public-key="<replace_with_public_key>"
    :access-token="accessToken"
    :routes="routes"
  >
    <RouterView />
  </Vable>
</template>

<script setup>
import { onMounted, ref } from "vue";
import { Vable } from "@vableai/vue/init";
import { routes } from "./router";

const accessToken = ref();

onMounted(async () => {
  const response = await fetch("/api/vable-token", { credentials: "include" });
  if (response.ok) {
    accessToken.value = (await response.json()).token;
  }
});
</script>

Token expiry and sign-out

VableAI checks the token each time a conversation starts or resumes. A conversation that's already running isn't interrupted when the token expires.

Renewing a token: Fetch a new token before the old one expires and update accessToken. The SDK sends the new value to the widget.

Signing out: Reload the page when a user signs out. Clearing accessToken doesn't remove the token from a widget that's already loaded.


Troubleshooting

An invalid token stops the conversation from starting. VableAI doesn't fall back to an anonymous conversation.

flowchart TD
  A[Conversation starts] --> B{Token passed?}
  B -- No --> C[Anonymous conversation]
  B -- Yes --> D{Signing key uploaded?}
  D -- No --> X[Conversation fails to start]
  D -- Yes --> E{Signature valid and token not expired?}
  E -- No --> X
  E -- Yes --> F{Has a sub claim?}
  F -- No --> X
  F -- Yes --> G[Conversation linked to the user]
ProblemLikely cause
The conversation doesn't startNo signing key is uploaded for this project, or the token expired
The conversation doesn't start after you replaced the signing keyThe server is still signing with the old private key
The conversation doesn't start with a valid-looking tokenThe token isn't signed with RS256, or it has no sub claim
The widget doesn't greet the user by nameThe token has no firstName claim