Skip to content

Authentication & Security

Lucky Funatic uses different authentication mechanisms depending on who's making the request: players, minigame clients, the Funtico Platform, or internal services.

Authentication Methods

Consumer Method Details
Telegram Mini App (players) JWT Issued after Telegram initData validation
Minigame iframes JWT + API key Game-specific JWT + X-Api-Key header
Funtico Platform Bearer token Static token from LARAVEL_API_TOKEN env var
Support/Admin Basic auth Username + password from config
Marketing API API key api_key query parameter
Balloon WebSocket JWT Token passed as ?token= query param

Player Authentication Flow

Players authenticate through Telegram's Mini App protocol. The frontend never handles passwords -- identity is guaranteed by Telegram's cryptographic signature.

sequenceDiagram
    participant User as Player
    participant TG as Telegram
    participant FE as Mini App Frontend
    participant API as Lucky Funatic API

    User->>TG: Opens Lucky Funatic bot
    TG->>FE: Launches Mini App with initData
    FE->>API: POST /login (initData in body)
    API->>API: Validate HMAC-SHA256 signature
    API->>API: Extract user info (telegram_id, username, etc.)
    API->>API: Create or update user in MySQL
    API->>API: Initialize game state if new player
    API->>FE: Return JWT token (3-day expiry)
    FE->>API: All subsequent requests with Authorization: Bearer <JWT>

How Telegram Validation Works

When Telegram launches a Mini App, it sends initData -- a query string containing user info, auth date, and an HMAC-SHA256 hash. The backend validates this by:

  1. Computing the secret key: HMAC-SHA256("WebAppData", telegram_bot_token)
  2. Computing the expected hash from the data fields using that secret key
  3. Comparing the expected hash with the one Telegram provided

If the hashes match, the user identity is cryptographically verified by Telegram. No password, no OAuth flow, no third-party tokens -- Telegram guarantees the identity.

JWT Token

After successful validation, the API issues a JWT containing:

  • user_id (internal Lucky Funatic user ID)
  • telegram_id
  • role
  • Expiration (3 days from issue)

The JWT is signed with the server's JWT_SECRET and included in all subsequent requests as Authorization: Bearer <token>.

Minigame Authentication

Minigames run in iframes and need a two-layer auth system:

  1. API key -- a shared secret (X-Api-Key header) that identifies the minigame service itself. Validated against the MINIGAME_API_KEY config value.

  2. Player JWT -- when a player opens a minigame, the API generates a game-specific JWT via /toy-box/games/:gameID/iframe. This token is Base62-encoded for URL safety and contains both the user ID and the game ID.

The minigame client uses both to authenticate score submissions and play session starts.

Platform Integration Auth

Communication between Lucky Funatic and the Funtico Platform uses server-to-server bearer tokens:

  • Platform -> Lucky Funatic: Requests to /laravel/* endpoints include Authorization: Bearer <LARAVEL_API_TOKEN>
  • Lucky Funatic -> Platform: Outgoing requests include Authorization: Bearer <PLATFORM_API_BEARER>

Both tokens are static secrets stored in environment variables.

Middleware Stack

Each route group applies a specific middleware chain. Here's how the main game routes are protected:

flowchart TD
    Request["Incoming Request"] --> CORS["CORS Middleware\n(validate origin)"]
    CORS --> JWT["JWT Middleware\n(validate token, extract user_id)"]
    JWT --> Session["Track Session\n(record activity in Redis)"]
    Session --> GameState["Ensure Game State\n(load from Redis, persist if stale)"]
    GameState --> Handler["Route Handler"]

The EnsureGameState middleware is worth noting: on every game route request, it pulls the player's game state from Redis and makes it available to the handler. If the state hasn't been persisted to MySQL in over 60 seconds, it triggers a sync. This means the handler always has fresh state available without explicit loading.

Route Group Middleware Summary

Route Group Auth CORS Extra
/ (game) JWT Configurable origins Session tracking, game state loading
/login None Permissive (all headers) --
/public None Platform origins only (funtico.com) --
/minigames API key + JWT Configurable origins --
/laravel Bearer token None --
/balloons JWT (query param) Configurable origins WebSocket upgrade check
/support Basic auth Configurable origins Action logging, feature flag gated
/ppc API key (query param) Configurable origins --
/metrics Basic auth None --

Security Notes

  • All game logic runs server-side. The client sends tap counts and actions, but the server validates everything (energy availability, currency balances, booster eligibility, tournament scores)
  • Tapping is capped at 50 taps per request to prevent abuse
  • Distributed locks (Redsync) prevent race conditions on currency operations
  • Lua scripts in Redis ensure atomic read-modify-write operations
  • Balloon game tap positions are validated server-side with a leniency bonus for network latency
  • Support endpoints are gated behind both a config flag (EnableSupportEndpoints) and Basic auth credentials