Authentication
TMA.sh authentication turns Telegram’s initData string into a verified user identity and a signed JWT. No passwords, no OAuth flows — the user is already authenticated by Telegram.
How it works
Section titled “How it works”- Telegram injects
initDatainto the WebApp context when a user opens your Mini App. - Your app sends
initDatato TMA.sh via the SDK. - TMA.sh validates the HMAC-SHA256 signature against your bot token, confirming the data came from Telegram and has not been tampered with.
- TMA.sh returns a signed JWT containing the user’s Telegram identity.
- Your app uses that JWT to authenticate API requests to your own backend or third-party services.
The entire flow is a single function call on the client.
Client-side usage
Section titled “Client-side usage”import { createTMA } from '@tma.sh/sdk';
const tma = createTMA({ projectId: 'your-project-id' });
const { user, jwt } = await tma.auth.validate( window.Telegram.WebApp.initData, 'your-project-id');
// user: { telegramId, firstName, lastName, username }// jwt: signed token (24h expiry)The returned jwt is a compact JWS signed with your project’s key. Include it as a Bearer token in requests to your API routes or external backends.
JWT claims
Section titled “JWT claims”Every JWT issued by TMA.sh includes the following claims:
| Claim | Description | Example |
|---|---|---|
sub | Subject identifier | tg_123456789 |
telegramId | Telegram user ID | 123456789 |
firstName | User’s first name | Alice |
lastName | User’s last name (may be empty) | Smith |
username | Telegram username (may be empty) | alicesmith |
projectId | TMA.sh project ID | proj_abc123 |
iat | Issued at (Unix timestamp) | 1700000000 |
exp | Expiration (Unix timestamp, 24h) | 1700086400 |
The sub claim is prefixed with tg_ to avoid collisions when integrating with external auth systems.
React hook
Section titled “React hook”The useTelegramAuth hook handles the full auth lifecycle — validation, loading state, and error handling:
import { TMAProvider, useTelegramAuth } from '@tma.sh/sdk/react';
function App() { return ( <TMAProvider config={{ projectId: 'your-project-id' }}> <Profile /> </TMAProvider> );}
function Profile() { const { user, jwt, isLoading, error } = useTelegramAuth();
if (isLoading) return <div>Loading...</div>; if (error) return <div>Auth failed: {error.message}</div>;
return <div>Welcome, {user.firstName}!</div>;}Wrap your app in <TMAProvider> once at the root, passing your projectId via the config prop. The provider initializes the SDK and makes auth state available to all child components.
Svelte store
Section titled “Svelte store”Initialize the TMA provider once in your root layout, then use the getTMAAuth function to access the reactive auth store in any component:
<script> import { initTMAProvider, getTMAAuth } from '@tma.sh/sdk/svelte';
// Call once in root layout initTMAProvider({ projectId: 'your-project-id' });
// In components, get the auth store const auth = getTMAAuth();</script>
{#if $auth.isLoading} <p>Loading...</p>{:else if $auth.user} <p>Welcome, {$auth.user.firstName}!</p>{/if}The store subscribes to auth state changes and updates the UI automatically. Check $auth.user to determine if the user is authenticated — there is no isAuthenticated property.
Supabase integration
Section titled “Supabase integration”TMA.sh JWTs work directly with Supabase Row Level Security. Set your project’s JWT secret in the TMA.sh dashboard (Settings > Auth > JWT Secret), then pass the token to the Supabase client:
import { createClient } from '@supabase/supabase-js';
const supabase = createClient( 'https://your-project.supabase.co', 'your-anon-key', { global: { headers: { Authorization: `Bearer ${jwt}` }, }, });
// RLS policies can use auth.uid() which returns 'tg_123456789'const { data } = await supabase .from('profiles') .select('*') .eq('user_id', user.telegramId);This pattern works with any backend that supports JWT verification — Firebase, Turso, or your own service.
JWKS endpoint
Section titled “JWKS endpoint”TMA.sh exposes a single JWKS endpoint for custom backend verification:
https://api.tma.sh/.well-known/jwks.jsonUse this to verify JWTs from your own server without sharing secrets. Most JWT libraries support JWKS out of the box:
import { createRemoteJWKSet, jwtVerify } from 'jose';
const JWKS = createRemoteJWKSet( new URL('https://api.tma.sh/.well-known/jwks.json'));
const { payload } = await jwtVerify(token, JWKS);// payload.sub === 'tg_123456789'Server-side middleware
Section titled “Server-side middleware”For API routes deployed on TMA.sh, use the requireUser() middleware instead of manual JWT verification. It extracts the Bearer token, verifies it via JWKS, and injects the user into the Hono context.
See Server Helpers for usage.