Authentication
When Telegram opens your Mini App, it passes signed initData to the WebView. TMA.sh validates this data server-side and returns a JWT you can use to authenticate requests to your own backend or third-party services like Supabase.
How it works
Section titled “How it works”- Telegram opens your Mini App and injects
initDatainto the WebView - Your app sends
initDatato the TMA.sh auth endpoint via the SDK - TMA.sh validates the signature against your bot token
- A signed JWT is returned containing the Telegram user’s identity
Client-side validation
Section titled “Client-side validation”Use the SDK to validate initData and get a JWT:
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');
console.log(user.telegramId); // 123456789console.log(user.firstName); // "Alice"console.log(user.username); // "alice"The returned jwt is a signed token you can attach to subsequent requests:
const response = await fetch('https://myapp--api.tma.sh/api/profile', { headers: { Authorization: `Bearer ${jwt}`, },});JWT claims
Section titled “JWT claims”The JWT issued by TMA.sh contains the following claims:
| Claim | Description | Example |
|---|---|---|
sub | Unique 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) | alice |
projectId | Your TMA.sh project ID | proj_abc123 |
iat | Issued at (Unix timestamp) | 1700000000 |
exp | Expires at (24 hours after issuance) | 1700086400 |
Server-side middleware
Section titled “Server-side middleware”Protect your API routes with the requireUser() middleware:
import { Hono } from 'hono';import { requireUser } from '@tma.sh/sdk/server';
const app = new Hono();
app.use('/api/protected/*', requireUser());
app.get('/api/protected/profile', (c) => { const user = c.get('user'); return c.json({ telegramId: user.telegramId, username: user.username, });});
export default app;The middleware verifies the JWT signature, checks expiration, and attaches the decoded user to the request context. Unauthorized requests receive a 401 response.
Supabase integration
Section titled “Supabase integration”You can use the TMA.sh JWT directly with Supabase by configuring a custom JWT secret.
Step 1: In the TMA.sh dashboard, go to your project’s Settings and copy the JWT signing secret.
Step 2: In your Supabase dashboard, go to Settings > API and set the JWT secret to the same value.
Step 3: Pass the JWT to the Supabase client:
import { createClient } from '@supabase/supabase-js';import { createTMA } from '@tma.sh/sdk';
const tma = createTMA({ projectId: 'your-project-id' });
const { jwt } = await tma.auth.validate( window.Telegram.WebApp.initData, 'your-project-id');
const supabase = createClient( 'https://your-project.supabase.co', 'your-anon-key', { global: { headers: { Authorization: `Bearer ${jwt}` }, }, });
// Supabase RLS policies now see the Telegram user as `auth.jwt().sub`const { data } = await supabase .from('profiles') .select('*') .eq('telegram_id', 'tg_123456789');This lets you use Supabase Row Level Security (RLS) with Telegram identities without managing a separate user table.
JWKS endpoint
Section titled “JWKS endpoint”For custom backend verification, TMA.sh exposes a JWKS (JSON Web Key Set) endpoint:
https://api.tma.sh/.well-known/jwks.jsonThis is a global endpoint (not per-project). Use it to verify JWTs in any language or framework that supports JWKS:
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'Framework helpers
Section titled “Framework helpers”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>Error: {error.message}</div>; return <div>Welcome, {user.firstName}!</div>;}Svelte
Section titled “Svelte”<script> import { initTMAProvider, getTMAAuth } from '@tma.sh/sdk/svelte';
// Call once in root layout initTMAProvider({ projectId: 'your-project-id' });
// 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}Security considerations
Section titled “Security considerations”- JWTs expire after 24 hours. Re-validate
initDatato get a fresh token. - Never expose your bot token or JWT signing secret in client-side code.
- Always verify JWTs server-side before trusting user identity.
- Use HTTPS for all API requests (TMA.sh enforces this by default).