Server-side token generation (SSO)¶
The widget requests a signed token from your backend (data-token-url). Traveln verifies it on /sso-login/ and creates a session.
Token endpoint¶
Implement a POST endpoint (for example: /api/traveln/sso/) that returns:
{ "token": "..." }
The widget sends:
{ "prompt": "string", "trace_id": "string" }
Token format¶
This is not a JWT.
The token is:
base64url(payload_json_bytes) + "." + base64url(hmac_sha256(secret, payload_json_bytes))
Required payload fields¶
tenant_slug(string)user_id(string)ts(integer, unix seconds)nonce(string, unique per token)
Optional payload fields¶
first_name,last_name,email,phone,picturehost(string): when present, Traveln binds the token to both the request host and the token host (both must be registered for the tenant).is_anonymous(bool/string): when true or identity is missing, Traveln may create a “Guest User” session.
Replay protection
Nonces are one-time use and timestamps are short-lived. Do not reuse tokens.
Example (Python)¶
import base64
import hmac
import json
import time
from hashlib import sha256
from uuid import uuid4
def b64url(data: bytes) -> str:
return base64.urlsafe_b64encode(data).decode("utf-8").rstrip("=")
def create_traveln_sso_token(*, tenant_slug: str, user_id: str, secret: str, host: str | None = None) -> str:
payload = {
"tenant_slug": tenant_slug,
"user_id": user_id,
"ts": int(time.time()),
"nonce": uuid4().hex,
}
if host:
payload["host"] = host
payload_bytes = json.dumps(payload, separators=(",", ":"), ensure_ascii=False).encode("utf-8")
sig = hmac.new(secret.encode("utf-8"), payload_bytes, sha256).digest()
return f"{b64url(payload_bytes)}.{b64url(sig)}"