After your frontend SDK returns a successful authentication result, the user object contains a proof string — a signed JWT produced by Bondify. Before you create a session, set a cookie, or issue a token for the user, you must verify this proof on your server using @bondify/server. Skipping this step allows a malicious client to fabricate a login without ever touching Telegram.
Installation
npm install @bondify/server
The package ships with full TypeScript type definitions and has no runtime dependencies beyond Node’s built-in crypto module.
verifyProof(proof, secretKey)
Cryptographically verifies a Bondify session proof using your project’s secret key. Returns a Promise that resolves to the verified user object. Throws if the proof is invalid, tampered with, or already used.
import { verifyProof } from '@bondify/server';
const user = await verifyProof(
proof, // string — from client's user.proof
process.env.BONDIFY_SECRET_KEY // string — your project's secret key
);
// Verified user object:
// {
// telegramId: string — the verified Telegram user ID
// name: string — the user's Telegram display name
// username: string | null — @handle, or null if not set
// }
Parameters
The proof string received from the client SDK after a successful signIn() call. Pass it directly from the request body — do not parse, decode, or modify it before passing it here.
Your project’s secret key from the Bondify dashboard. Store this in a server-side environment variable. Never expose it to the browser.
Return value
On success, verifyProof resolves with a plain object:
| Field | Type | Description |
|---|
telegramId | string | The verified Telegram user ID |
name | string | The user’s Telegram display name |
username | string | null | The user’s @handle, or null if they have none set |
Error handling
verifyProof throws a standard Error in any of these situations:
- The proof signature is invalid (the secret key does not match)
- The proof payload has been tampered with
- The proof has already been used (replay protection)
- The proof is malformed or cannot be parsed
Always wrap the call in a try / catch and respond with 401 on failure:
import { verifyProof } from '@bondify/server';
export async function POST(req) {
try {
const { proof } = await req.json();
const user = await verifyProof(proof, process.env.BONDIFY_SECRET_KEY);
// Safe to log the user in — create a session, cookie, or JWT here
const session = await createUserSession(user.telegramId);
return Response.json({ ok: true, sessionId: session.id });
} catch (err) {
// Proof is invalid, tampered, or expired
return Response.json({ error: 'Authentication failed' }, { status: 401 });
}
}
Never skip proof verification. Always call verifyProof on your server before creating a user session, issuing a JWT, or granting access to protected resources. The client-side isAuthenticated state and user object cannot be trusted without server-side verification.
Framework examples
Next.js (App Router)
Express
Fastify
// app/api/auth/verify/route.ts
import { verifyProof } from '@bondify/server';
import { cookies } from 'next/headers';
export async function POST(req: Request) {
const { proof } = await req.json();
try {
const user = await verifyProof(proof, process.env.BONDIFY_SECRET_KEY!);
// Set a session cookie
cookies().set('session_user', user.telegramId, {
httpOnly: true,
secure: true,
sameSite: 'lax',
maxAge: 60 * 60 * 24 * 7, // 7 days
});
return Response.json({ ok: true });
} catch {
return Response.json({ error: 'Unauthorized' }, { status: 401 });
}
}
// routes/auth.ts
import express from 'express';
import { verifyProof } from '@bondify/server';
const router = express.Router();
router.post('/verify', async (req, res) => {
const { proof } = req.body;
try {
const user = await verifyProof(proof, process.env.BONDIFY_SECRET_KEY!);
// Store the verified user in your session or DB
req.session.userId = user.telegramId;
res.json({ ok: true, name: user.name });
} catch {
res.status(401).json({ error: 'Authentication failed' });
}
});
export default router;
// plugins/auth.ts
import { FastifyInstance } from 'fastify';
import { verifyProof } from '@bondify/server';
export async function authPlugin(fastify: FastifyInstance) {
fastify.post('/api/auth/verify', async (request, reply) => {
const { proof } = request.body as { proof: string };
try {
const user = await verifyProof(proof, process.env.BONDIFY_SECRET_KEY!);
const token = fastify.jwt.sign({ telegramId: user.telegramId });
return { ok: true, token };
} catch {
reply.status(401).send({ error: 'Authentication failed' });
}
});
}
Environment variable setup
Find your secret key
Open the Bondify dashboard, navigate to Projects, and copy the secret key for the relevant project.
Store it as a server-only env var
Add it to your .env file (or your hosting provider’s secret store):BONDIFY_SECRET_KEY=sk_live_xxxxxxxxxxxxxxxxxxxxxxxx
In Next.js, do not prefix it with NEXT_PUBLIC_ — that would expose it to the browser bundle. Reference it in your verification call
const user = await verifyProof(proof, process.env.BONDIFY_SECRET_KEY!);
If you suspect your secret key has been exposed, rotate it immediately from the dashboard under Projects → Regenerate key, then redeploy with the updated environment variable. Old proofs signed with the previous key will begin failing immediately.