Skip to content

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, picture
  • host (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)}"