Cookiebot
Features

OAuth2 token issuer for the Telegram Mini App

net-new: v1 had no Mini App. /oauth2/token with three grants (Telegram initData, the login widget, refresh_token), the same RS256 keys and JWKS /login already publishes, plus scopes, a short access-token life and rotating refresh tokens stored as hashes (migration 0010). A replayed refresh token revokes its whole family. /login is untouched and its scope-less token stays read-only

DoneM4apiplatform
v1 scenarios
—
Ported
—
Green
—
Failing
—

net-new: v1 had no Mini App. /oauth2/token with three grants (Telegram initData, the login widget, refresh_token), the same RS256 keys and JWKS /login already publishes, plus scopes, a short access-token life and rotating refresh tokens stored as hashes (migration 0010). A replayed refresh token revokes its whole family. /login is untouched and its scope-less token stays read-only

What it does

A Mini App opens inside Telegram with initData — a signed string naming the user who opened it. POST /oauth2/token exchanges that for a short-lived RS256 access token and a refresh token, so every later call is an ordinary bearer request. The same endpoint takes the web console's login-widget payload, and a refresh token, as two more grants; the access token is the one /login already mints, signed by the same keys and published by the same JWKS, so a resource server verifies both without learning a second format.

The full flow, the request and response shapes and the scopes are on Mini App API.

Behaviour that must not change

/login is untouched. It is v1's endpoint, the shipped web console posts to it, and its token still has no scope claim — which cb_api.security reads as read-only. A console that could only read before this feature cannot write because of it.

The discovery document only grew. /.well-known/openid-configuration carries v1's five keys with v1's values; the token endpoint, the grant list and the scopes are additions beside them.

One answer for every failed grant. A bad signature, a stale auth_date and a payload with no user in it all return invalid_grant. A client that could tell them apart could probe which forgery got closest.

How to verify it

packages/cb-api/tests/test_miniapp_auth.py covers Telegram's algorithm — the WebAppData key derivation, the two fields outside the signature, the replay window — and test_oauth_endpoints.py covers the three grants over HTTP. Rotation, replay detection and expiry are proved against real rows in qa/integration/test_refresh_tokens.py, including the two-concurrent-refreshes race the used_at IS NULL predicate exists for.

On this page