You want to help build a Telegram auth plugin. Telegram, the platform where bots talk to bots about talking to bots. Sounds about right. Come on in.
git clone https://github.com/vcode-sh/better-auth-telegram.git
cd better-auth-telegram
npm install- Node.js >= 24 (see
.nvmrc-- yes, I pinned it, I'm not an animal) - npm 11.18.0 (the version used in CI)
These are the incantations. Learn them. Love them. Run them before pushing.
npm run dev # Watch mode build (tsdown) -- for the impatient
npm run build # Production build (tsdown: ESM + CJS + DTS, checked by publint + attw) -- the real thing
npm run type-check # TypeScript strict check -- the compiler judges you so I don't have to
npm run test:types # Type-check a consumer of the built dist/ (run build first)
npm test # Run all tests -- you'd be surprised how many people skip this
npm run test:watch # Watch mode -- for when you're in the zone
npm run test:ui # Vitest UI -- pretty graphs that prove you did something
npm run test:coverage # Coverage report -- the number must go up
npm run test:adapters # Real PostgreSQL / MySQL / MongoDB (Docker); databases without a BAT_TEST_*_URL are skipped, see vitest.adapters.config.ts
npm run lint # Biome lint check -- it has opinions and they are correct
npm run lint:fix # Auto-fix -- let the machine do the boring partsrc/
index.ts Server plugin entry (endpoints, schema, init hook)
client.ts Client plugin (widget init, API methods, Mini App helpers)
plugin-config.ts Option parsing, defaults, startup warnings
schema.ts Conditional user/account schema fields
widget-endpoints.ts /telegram/signin, /telegram/link, /telegram/unlink
miniapp-endpoints.ts /telegram/miniapp/signin, /telegram/miniapp/validate
config-endpoint.ts /telegram/config
telegram-account.ts Find/link/create/unlink through Better Auth's internal adapter
oidc.ts telegram-oidc provider (PKCE, nonce, Basic auth, ID token verification)
telegram-identity.ts databaseHooks: placeholder email guard, OIDC claims onto user/account,
re-checking user.telegramId when a Telegram account is deleted
verify.ts HMAC-SHA-256 verification via Web Crypto API
types.ts TypeScript interfaces
constants.ts Error codes, defaults, PLUGIN_ID, OIDC endpoints
testing/harness.ts Real Better Auth (memory or SQLite) plus a fake oauth.telegram.org
*.test.ts Co-located unit tests
*.integration.test.ts Tests against a real Better Auth instance
test-d/ Type tests for consumers of dist/
Not guidelines. Not suggestions. Rules.
- Tests live next to their code as
*.test.ts-- no hunting through a distant__tests__folder like it's 2017 - Run
npm run lint:fixbefore committing -- Biome catches things your eyes won't - All tests must pass with
npm test-- a failing test suite is not a "known issue," it's a blocker - Type safety everywhere -- no
anyat public API boundaries, this isn't JavaScript - Security first, features second -- validate inputs, verify signatures, no shortcuts
- Async verification only -- all crypto goes through
crypto.subtle, no synchronousnode:crypto - Use
APIErrorfrombetter-auth/api-- rawctx.json({ error })died in v0.4.0, let it rest - Write users and accounts through
ctx.context.internalAdapter-- rawctx.context.adapter.createskipsdatabaseHooksanduser.validateUserInfo, which is exactly the v2 bug v3 fixed. Reads viaadapter.findOneare fine - Update CHANGELOG.md for user-facing changes -- the changelog is a love letter to your future maintainers
I maintain 90%+ coverage. Not because I worship the metric, but because untested code is just a theory.
- Verification: crypto tests in
verify.test.tsandverify-hardening.test.ts-- HMAC paths, replay attacks, futureauth_date, hash format, edge cases - Server plugin:
widget.integration.test.ts,oidc.integration.test.tsandidentity.integration.test.tsrun a realbetterAuth()(memory adapter, or SQLite with the schema Better Auth's migrations generate) against a fake Telegram modeled on the live one -- signin, link, unlink, Mini App, OIDC, hooks,validateUserInfo, identity revocation, placeholder squatting. Prefer these over mocking Better Auth: mock-only tests passed while OIDC was broken on 1.7 - Client plugin: mock
$fetch, test widget init and API methods inclient.test.ts - Security: adversarial inputs, timestamp manipulation, malformed data, forged ID tokens
- Fork the repo and branch off
main(notdevelop, notfeature-branch-from-six-months-ago) - Write tests for your new code (yes, before the PR, not "I'll add them later")
- Make sure absolutely everything passes:
npm run type-check && npm test && npm run lint
- Update documentation if applicable (it is applicable more often than you think)
- Update
CHANGELOG.mdunder an[Unreleased]section - Open a PR with a clear description -- "misc fixes" tells me nothing
- Bugs: use the bug report template
- Features: use the feature request template
- Security: see SECURITY.md -- do NOT open a public issue for vulnerabilities unless you enjoy chaos
There is one. It's in CODE_OF_CONDUCT.md. The short version: don't be awful. The long version: read the file.
Node.js 24 is the minimum. The required CI check collects package validation, Node 24.0.0 and current 24/26 tests, the complete Better Auth 1.7.12 family, forced Bun 1.4.4 tests and real database adapters. A failed or skipped required lane fails the gate. Fork PRs run with read-only permissions.
npm run build checks the bundles and declarations with publint and Are The Types Wrong. npm run test:package packs the result, installs it outside the checkout, checks its consumer types, exercises ESM authentication configuration and loads both CommonJS exports. It leaves better-auth-telegram-<version>.tgz for inspection. CI uploads that tarball.
A matching v<version> tag on a commit belonging to main runs the same CI before release. Only the separate publisher receives npm OIDC permission; it publishes the validated tarball with provenance, without rebuilding or running lifecycle scripts. Stable versions use latest, prereleases use next. Creating a PR does not publish a release.
Codecov uses the same 90% coverage floor as Vitest. Coverage is a guardrail; add tests that challenge observable behavior rather than tests whose only purpose is reaching 100%.