Skip to content

Authentication ​

When users are already logged into your application, you can pass their identity to Klero using a JWT token. This lets them submit feedback, vote, and comment without creating a separate Klero account.

How It Works ​

  1. Generate a JWT on your server, signed with your project's JWT secret
  2. Pass that token to the Klero widget/SDK via the customerToken option
  3. Klero verifies the token and associates actions with that user

Finding Your JWT Secret ​

  1. Open your project in Klero
  2. Go to Settings → JWT Secret
  3. Use the eye icon to reveal it and the copy button to copy it

Keep this secret secure. Never put it in client-side code or version control.

Token Payload ​

The JWT payload must be a JSON object with:

FieldRequiredDescription
idYesYour user's unique ID (string or number)
emailYesUser's email address
nameNoDisplay name
avatarNoURL to the user's avatar image
blockedNoSet to true to block the user (default: false)
expRecommendedToken expiration as Unix timestamp

Tokens must be signed with HS256.

React Native ​

Installation ​

bash
npm install @kleroai/react-native react-native-webview jwt-encode

Generate Token ​

typescript
import { sign } from 'jwt-encode';

const JWT_SECRET = 'your-jwt-secret'; // From your backend or secure storage

function generateCustomerToken(user: { id: string; email: string; name?: string; avatar?: string }) {
  const payload = {
    id: user.id,
    email: user.email,
    name: user.name ?? null,
    avatar: user.avatar ?? null,
    exp: Math.floor(Date.now() / 1000) + 24 * 60 * 60, // 24 hours
  };

  return sign(payload, JWT_SECRET);
}

Pass Token to SDK ​

tsx
import { KleroSurvey } from '@kleroai/react-native';

function App() {
  const user = useCurrentUser();
  const customerToken = generateCustomerToken(user);

  return (
    <KleroSurvey
      projectSlug="myproject"
      surveyUlid="01ABC123..."
      customerToken={customerToken}
      onComplete={(data) => console.log('Completed:', data)}
    />
  );
}

Update Token Dynamically ​

tsx
import { useRef } from 'react';
import { KleroWidget, KleroWidgetHandle } from '@kleroai/react-native';

function App() {
  const widgetRef = useRef<KleroWidgetHandle>(null);

  const handleLogin = (user) => {
    const token = generateCustomerToken(user);
    widgetRef.current?.setCustomerToken(token);
  };

  return <KleroWidget ref={widgetRef} projectSlug="myproject" />;
}

Android ​

Installation ​

Add to your build.gradle.kts:

kotlin
dependencies {
    implementation("com.github.klero-ai:klero-android:0.1.0")
    implementation("com.auth0:java-jwt:4.4.0")
}

Generate Token ​

kotlin
import com.auth0.jwt.JWT
import com.auth0.jwt.algorithms.Algorithm
import java.util.Date

object KleroAuth {
    private const val JWT_SECRET = "your-jwt-secret" // From secure storage

    fun generateCustomerToken(
        id: String,
        email: String,
        name: String? = null,
        avatar: String? = null
    ): String {
        val algorithm = Algorithm.HMAC256(JWT_SECRET)
        val expiresAt = Date(System.currentTimeMillis() + 24 * 60 * 60 * 1000) // 24 hours

        return JWT.create()
            .withClaim("id", id)
            .withClaim("email", email)
            .withClaim("name", name)
            .withClaim("avatar", avatar)
            .withExpiresAt(expiresAt)
            .sign(algorithm)
    }
}

Pass Token to SDK ​

kotlin
import com.klero.sdk.KleroConfig
import com.klero.sdk.KleroSurvey

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)

        val user = getCurrentUser()
        val customerToken = KleroAuth.generateCustomerToken(
            id = user.id,
            email = user.email,
            name = user.name,
            avatar = user.avatarUrl
        )

        val config = KleroConfig(
            projectSlug = "myproject",
            customerToken = customerToken
        )

        val survey = KleroSurvey(this, config, "01ABC123...")
        setContentView(survey)
    }
}

Jetpack Compose ​

kotlin
@Composable
fun AuthenticatedSurvey() {
    val user = remember { getCurrentUser() }
    val customerToken = remember(user) {
        KleroAuth.generateCustomerToken(
            id = user.id,
            email = user.email,
            name = user.name
        )
    }

    KleroSurveyComposable(
        projectSlug = "myproject",
        surveyUlid = "01ABC123...",
        customerToken = customerToken,
        onComplete = { _, responseUlid, _ ->
            println("Completed: $responseUlid")
        }
    )
}

iOS ​

Installation ​

Add to your Package.swift:

swift
dependencies: [
    .package(url: "https://github.com/klero-ai/klero-ios.git", from: "0.1.0"),
    .package(url: "https://github.com/IBM-Swift/Swift-JWT.git", from: "4.0.0")
]

Generate Token ​

swift
import Foundation
import SwiftJWT

struct KleroCustomerClaims: Claims {
    let id: String
    let email: String
    let name: String?
    let avatar: String?
    let exp: Date
}

class KleroAuth {
    private static let jwtSecret = "your-jwt-secret" // From secure storage

    static func generateCustomerToken(
        id: String,
        email: String,
        name: String? = nil,
        avatar: String? = nil
    ) -> String? {
        let claims = KleroCustomerClaims(
            id: id,
            email: email,
            name: name,
            avatar: avatar,
            exp: Date().addingTimeInterval(24 * 60 * 60) // 24 hours
        )

        var jwt = JWT(claims: claims)
        let signer = JWTSigner.hs256(key: Data(jwtSecret.utf8))

        return try? jwt.sign(using: signer)
    }
}

Pass Token to SDK (UIKit) ​

swift
import UIKit
import KleroSDK

class ViewController: UIViewController, KleroSurveyDelegate {

    private var survey: KleroSurvey!

    override func viewDidLoad() {
        super.viewDidLoad()

        let user = getCurrentUser()
        guard let customerToken = KleroAuth.generateCustomerToken(
            id: user.id,
            email: user.email,
            name: user.name,
            avatar: user.avatarUrl
        ) else { return }

        let config = KleroConfig(
            projectSlug: "myproject",
            customerToken: customerToken
        )

        survey = KleroSurvey(config: config, surveyUlid: "01ABC123...")
        survey.delegate = self
        view.addSubview(survey)
    }
}

SwiftUI ​

swift
import SwiftUI
import KleroSDK

struct AuthenticatedSurveyView: View {
    let user: User

    var customerToken: String? {
        KleroAuth.generateCustomerToken(
            id: user.id,
            email: user.email,
            name: user.name
        )
    }

    var body: some View {
        if let token = customerToken {
            KleroSurveyView(
                projectSlug: "myproject",
                surveyUlid: "01ABC123...",
                customerToken: token,
                onComplete: { _, responseUlid, _ in
                    print("Completed: \(responseUlid)")
                }
            )
        }
    }
}

PHP ​

Installation ​

bash
composer require firebase/php-jwt

Generate Token ​

php
<?php

use Firebase\JWT\JWT;

class KleroAuth
{
    public static function generateCustomerToken(
        string $id,
        string $email,
        ?string $name = null,
        ?string $avatar = null
    ): string {
        $jwtSecret = getenv('KLERO_JWT_SECRET');

        $payload = [
            'id'     => $id,
            'email'  => $email,
            'name'   => $name,
            'avatar' => $avatar,
            'exp'    => time() + (24 * 60 * 60), // 24 hours
        ];

        return JWT::encode($payload, $jwtSecret, 'HS256');
    }
}

// Usage
$user = auth()->user();
$token = KleroAuth::generateCustomerToken(
    id: (string) $user->id,
    email: $user->email,
    name: $user->name,
    avatar: $user->avatar_url
);

Pass Token to Widget ​

html
<klero-feedback data-config='{
    "customerToken": "<?= $token ?>"
}'></klero-feedback>

<script src="https://your-project.klero.ai/embed/klero.js" async></script>

Python ​

Installation ​

bash
pip install pyjwt

Generate Token ​

python
import jwt
import os
from datetime import datetime, timedelta

class KleroAuth:
    JWT_SECRET = os.environ["KLERO_JWT_SECRET"]

    @staticmethod
    def generate_customer_token(
        id: str,
        email: str,
        name: str = None,
        avatar: str = None
    ) -> str:
        payload = {
            "id": id,
            "email": email,
            "name": name,
            "avatar": avatar,
            "exp": datetime.utcnow() + timedelta(hours=24),
        }
        return jwt.encode(payload, KleroAuth.JWT_SECRET, algorithm="HS256")


# Usage
user = request.user
token = KleroAuth.generate_customer_token(
    id=str(user.id),
    email=user.email,
    name=user.name,
    avatar=user.avatar_url
)

Pass Token to Widget (Django) ​

html
<klero-feedback data-config='{
    "customerToken": "{{ token }}"
}'></klero-feedback>

<script src="https://your-project.klero.ai/embed/klero.js" async></script>

Node.js ​

Installation ​

bash
npm install jsonwebtoken

Generate Token ​

javascript
const jwt = require('jsonwebtoken');

const JWT_SECRET = process.env.KLERO_JWT_SECRET;

function generateCustomerToken({ id, email, name, avatar }) {
  const payload = {
    id: String(id),
    email,
    name: name ?? null,
    avatar: avatar ?? null,
    exp: Math.floor(Date.now() / 1000) + 24 * 60 * 60, // 24 hours
  };

  return jwt.sign(payload, JWT_SECRET, { algorithm: 'HS256' });
}

// Usage
const token = generateCustomerToken({
  id: user.id,
  email: user.email,
  name: user.name,
  avatar: user.avatarUrl,
});

Pass Token to Widget (Express) ​

javascript
app.get('/feedback', (req, res) => {
  const token = generateCustomerToken(req.user);

  res.send(`
    <klero-feedback data-config='{"customerToken": "${token}"}'></klero-feedback>
    <script src="https://your-project.klero.ai/embed/klero.js" async></script>
  `);
});

TypeScript ​

typescript
import jwt from 'jsonwebtoken';

interface User {
  id: string | number;
  email: string;
  name?: string;
  avatar?: string;
}

function generateCustomerToken(user: User): string {
  const payload = {
    id: String(user.id),
    email: user.email,
    name: user.name ?? null,
    avatar: user.avatar ?? null,
    exp: Math.floor(Date.now() / 1000) + 24 * 60 * 60,
  };

  return jwt.sign(payload, process.env.KLERO_JWT_SECRET!, { algorithm: 'HS256' });
}

Security Best Practices ​

  1. Generate tokens only on the server – Never expose the JWT secret to clients
  2. Store the secret safely – Use environment variables or secure storage
  3. Use short expiration – 24 hours is a good default
  4. Only issue tokens for logged-in users – Verify authentication first
  5. Use HTTPS – Always serve over HTTPS when using customer tokens

Troubleshooting ​

Token not working ​

  • Ensure signed with HS256 and your project's JWT secret
  • Confirm payload includes id and email
  • Check token has not expired

User not recognized ​

  • Use a stable, unique id for each user
  • Ensure email is present and correct

Invalid or expired token ​

  • Regenerate token when user logs in or token expires
  • Each project has its own JWT secret

Customer Feedback Management