7.7 KiB
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
- Frontend opens
https://t.me/{botUsername}?start=auth_{callbackUrl}. - Telegram bot creates a session and sends the user a login button.
- The button points to
GET /userauth/telegram/callback?token={sessionId}. - Backend sets
userauth_sessioncookie and redirects back to the storefront. - Frontend calls
GET /userauth/sessionand becomes authenticated.
2. QR login from desktop
- Frontend opens dialog.
- Frontend calls
POST /userauth/qr/create. - Backend returns
{ token, url }whereurlis a Telegram deep link. - Frontend renders a QR from that URL.
- User scans QR and bot calls
POST /userauth/qr/confirm. - Frontend polls
GET /userauth/qr/poll?token=...every 3 seconds. - When status becomes
confirmed, backend returns session payload and sets the cookie. - Frontend syncs local cart using
POST /usersession/{sessionId}.
Session Shape
The frontend expects this exact response shape for the authenticated session.
{
"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:
{}
Response 200:
{
"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:
{ "status": "pending" }
Confirmed:
{
"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:
{ "status": "expired" }
Behavior:
- If confirmed, set cookie
userauth_sessionin 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:
X-Bot-Secret: <shared secret between bot and backend>
Request body:
{
"token": "dG9rZW4tYWJjMTIz",
"telegram_user": {
"id": 123456789,
"first_name": "Ivan",
"last_name": "Petrov",
"username": "ivan_petrov"
}
}
Response 200:
{ "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:
{
"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
tokenquery 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:
{}
Response 200:
{ "message": "ok" }
POST /usersession/{sessionId}
Synchronizes local cart immediately after successful login.
Request body:
[
{
"itemID": 123,
"quantity": 2,
"colour": "#ff0000",
"size": "XL",
"price": 1500
}
]
Notes:
- This payload is unchanged from the existing implementation.
priceis already discounted on the frontend side.- The frontend skips the call if cart is empty.
Telegram Deep Link Format
Direct login link format:
https://t.me/{botUsername}?start=auth_{urlEncodedCallbackUrl}
QR login link format:
https://t.me/{botUsername}?start=login_{qrToken}
Important limit:
- Telegram limits the
startpayload 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:
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
userauthendpoints and theusersessionsync endpoint. - Store sessions for 24 hours.
- Store QR tokens for 5 minutes.
- Protect
POST /userauth/qr/confirmwithX-Bot-Secret. - Set
userauth_sessioncookie on confirmed QR poll and direct callback. - Return the exact session JSON shape.
- Support credentialed CORS.
Bot Checklist
- Handle
/start login_{token}and callPOST /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-Secretvalue with backend.