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
- Generate a JWT on your server, signed with your project's JWT secret
- Pass that token to the Klero widget/SDK via the
customerTokenoption - Klero verifies the token and associates actions with that user
Finding Your JWT Secret
- Open your project in Klero
- Go to Settings → JWT Secret
- 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:
| Field | Required | Description |
|---|---|---|
id | Yes | Your user's unique ID (string or number) |
email | Yes | User's email address |
name | No | Display name |
avatar | No | URL to the user's avatar image |
blocked | No | Set to true to block the user (default: false) |
exp | Recommended | Token expiration as Unix timestamp |
Tokens must be signed with HS256.
React Native
Installation
bash
npm install @kleroai/react-native react-native-webview jwt-encodeGenerate 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-jwtGenerate 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 pyjwtGenerate 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 jsonwebtokenGenerate 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
- Generate tokens only on the server – Never expose the JWT secret to clients
- Store the secret safely – Use environment variables or secure storage
- Use short expiration – 24 hours is a good default
- Only issue tokens for logged-in users – Verify authentication first
- 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
idandemail - Check token has not expired
User not recognized
- Use a stable, unique
idfor each user - Ensure
emailis present and correct
Invalid or expired token
- Regenerate token when user logs in or token expires
- Each project has its own JWT secret