# Telegram UserAuth Backend Contract This document extracts the existing Telegram login flow into a repo-neutral contract for reuse in other projects. The UI behavior, payloads, polling cadence, and session model stay the same. Only route names and cookie naming are generalized. ## Endpoint Renaming | Current app contract | Reusable contract | |---|---| | `GET /auth/session` | `GET /userauth/session` | | `POST /auth/qr/create` | `POST /userauth/qr/create` | | `GET /auth/qr/poll?token=...` | `GET /userauth/qr/poll?token=...` | | `POST /auth/qr/confirm` | `POST /userauth/qr/confirm` | | `GET /auth/telegram/callback` | `GET /userauth/telegram/callback` | | `POST /auth/logout` | `POST /userauth/logout` | | `POST /websession/{sessionId}` | `POST /usersession/{sessionId}` | | Cookie `dx_session` | Cookie `userauth_session` | ## Flow Summary There are two supported flows. ### 1. Direct login from button 1. Frontend opens `https://t.me/{botUsername}?start=auth_{callbackUrl}`. 2. Telegram bot creates a session and sends the user a login button. 3. The button points to `GET /userauth/telegram/callback?token={sessionId}`. 4. Backend sets `userauth_session` cookie and redirects back to the storefront. 5. Frontend calls `GET /userauth/session` and becomes authenticated. ### 2. QR login from desktop 1. Frontend opens dialog. 2. Frontend calls `POST /userauth/qr/create`. 3. Backend returns `{ token, url }` where `url` is a Telegram deep link. 4. Frontend renders a QR from that URL. 5. User scans QR and bot calls `POST /userauth/qr/confirm`. 6. Frontend polls `GET /userauth/qr/poll?token=...` every 3 seconds. 7. When status becomes `confirmed`, backend returns session payload and sets the cookie. 8. Frontend syncs local cart using `POST /usersession/{sessionId}`. ## Session Shape The frontend expects this exact response shape for the authenticated session. ```json { "sessionId": "550e8400-e29b-41d4-a716-446655440000", "telegramUserId": 123456789, "username": "ivan_petrov", "displayName": "Ivan Petrov", "active": true, "expiresAt": "2026-05-21T14:30:00Z" } ``` | Field | Type | Required | Notes | |---|---|---|---| | `sessionId` | string | yes | Session identifier used in cart sync | | `telegramUserId` | number | yes | Telegram user ID | | `username` | string or null | no | Telegram username | | `displayName` | string | yes | User-facing full name | | `active` | boolean | yes | `false` means expired session | | `expiresAt` | ISO 8601 string | yes | Used by frontend refresh scheduling | Recommended TTL: - Session TTL: 24 hours - QR token TTL: 5 minutes ## HTTP Contract ### `POST /userauth/qr/create` Creates a one-time QR login token when the dialog opens. Request body: ```json {} ``` Response `200`: ```json { "token": "dG9rZW4tYWJjMTIz", "url": "https://t.me/userauth_bot?start=login_dG9rZW4tYWJjMTIz" } ``` Requirements: - Generate a cryptographically secure token. - Save token with status `pending`. - Return a Telegram deep link in `url`. - Rate limit to 5 requests per minute per IP. ### `GET /userauth/qr/poll?token={token}` Called every 3 seconds until confirmation or expiration. Possible responses: Pending: ```json { "status": "pending" } ``` Confirmed: ```json { "status": "confirmed", "session": { "sessionId": "550e8400-e29b-41d4-a716-446655440000", "telegramUserId": 123456789, "username": "ivan_petrov", "displayName": "Ivan Petrov", "active": true, "expiresAt": "2026-05-21T14:30:00Z" } } ``` Expired: ```json { "status": "expired" } ``` Behavior: - If confirmed, set cookie `userauth_session` in the response. - Delete or invalidate the QR token after the first successful confirmed poll. - If token is unknown or expired, return `status: "expired"`. ### `POST /userauth/qr/confirm` Internal endpoint called by the Telegram bot after the user scans the QR code. Required header: ```text X-Bot-Secret: ``` Request body: ```json { "token": "dG9rZW4tYWJjMTIz", "telegram_user": { "id": 123456789, "first_name": "Ivan", "last_name": "Petrov", "username": "ivan_petrov" } } ``` Response `200`: ```json { "status": "ok" } ``` Behavior: - Validate `X-Bot-Secret`. - Validate token exists and is still `pending`. - Create a user session. - Store session ID on the QR token. - Mark QR token as `confirmed`. ### `GET /userauth/session` Returns the currently active session based on the cookie. Frontend behavior depends on this endpoint in two places: - initial auth check on app startup - fallback polling if QR token creation fails Response `200`: ```json { "sessionId": "550e8400-e29b-41d4-a716-446655440000", "telegramUserId": 123456789, "username": "ivan_petrov", "displayName": "Ivan Petrov", "active": true, "expiresAt": "2026-05-21T14:30:00Z" } ``` Error handling: - Any non-200 response is treated by the frontend as unauthenticated. ### `GET /userauth/telegram/callback?token={sessionId}` Used for direct Telegram login from the primary button flow. Behavior: - Read the `token` query param. - Resolve it to a valid active session. - Set cookie `userauth_session`. - Redirect user to the storefront URL. ### `POST /userauth/logout` Clears the backend session and expires the cookie. Request body: ```json {} ``` Response `200`: ```json { "message": "ok" } ``` ### `POST /usersession/{sessionId}` Synchronizes local cart immediately after successful login. Request body: ```json [ { "itemID": 123, "quantity": 2, "colour": "#ff0000", "size": "XL", "price": 1500 } ] ``` Notes: - This payload is unchanged from the existing implementation. - `price` is already discounted on the frontend side. - The frontend skips the call if cart is empty. ## Telegram Deep Link Format Direct login link format: ```text https://t.me/{botUsername}?start=auth_{urlEncodedCallbackUrl} ``` QR login link format: ```text https://t.me/{botUsername}?start=login_{qrToken} ``` Important limit: - Telegram limits the `start` payload to 64 characters. - A base64url encoding of 32 random bytes plus `login_` fits safely. ## Cookie Requirements Use these cookie settings for the frontend to work correctly across site and API origins. | Property | Value | |---|---| | Name | `userauth_session` | | Path | `/` | | HttpOnly | `true` | | Secure | `true` | | SameSite | `None` | | MaxAge | `86400` | | Domain | your shared parent domain, for example `.example.com` | ## CORS Requirements Because the frontend sends credentials, backend must return an explicit origin. Required headers: ```text Access-Control-Allow-Origin: https://your-frontend.example Access-Control-Allow-Credentials: true Access-Control-Allow-Methods: GET, POST, OPTIONS Access-Control-Allow-Headers: Content-Type ``` Do not use `*` for `Access-Control-Allow-Origin` together with credentials. ## Frontend Runtime Expectations The current dialog behavior is fixed and should be preserved by backend responses. - QR polling interval: every 3 seconds - QR expiration on frontend: after 100 checks - If QR creation fails, frontend falls back to direct login URL and session polling - After login, frontend closes the dialog and re-checks session ## Minimal Backend Checklist - Implement all six `userauth` endpoints and the `usersession` sync endpoint. - Store sessions for 24 hours. - Store QR tokens for 5 minutes. - Protect `POST /userauth/qr/confirm` with `X-Bot-Secret`. - Set `userauth_session` cookie on confirmed QR poll and direct callback. - Return the exact session JSON shape. - Support credentialed CORS. ## Bot Checklist - Handle `/start login_{token}` and call `POST /userauth/qr/confirm`. - Handle `/start auth_{callbackUrl}` and provide a button that opens the callback URL. - Send success and expiration messages back to the user. - Share the same `X-Bot-Secret` value with backend.