A secure, scalable backend API for password management and sharing with multi-factor authentication support.
Built with modern technologies and designed for both Web2 and Web3 authentication paradigms.
- Overview
- Features
- Technology Stack
- Encryption & Security
- Architecture
- Getting Started
- API Reference
- Authentication
- Testing
- Deployment
- License
TACoSec is a NestJS-based server application that provides secure password/secret management and sharing capabilities. It uniquely supports dual authentication through both Telegram and EVM wallet signatures, making it suitable for both traditional Web2 users and Web3 crypto-native users.
Powered by TACo, TACoSec leverages cutting-edge cryptographic primitives to ensure your secrets remain secure and private.
The application enables users to:
- Securely store encrypted passwords and secrets
- Share secrets with other users via Telegram usernames or wallet addresses
- Manage multiple wallet public addresses per account
- Receive notifications about shared secrets
- Report malicious users with automatic restriction enforcement
- Encrypted Password Storage - AES-256-CBC encryption for all stored secrets
- Secret Sharing - Share passwords with other users securely
- Hierarchical Secrets - Support for parent-child secret relationships
- View Tracking - Track when shared secrets are viewed
- Telegram Authentication - Native Telegram WebApp init data validation
- JWT Authentication - Standard Bearer token authentication with refresh tokens
- Wallet Authentication - EVM signature verification using ethers.js
- Flexible Auth Guards - Endpoints can accept either auth method
- User Profiles - Linked Telegram accounts and wallet addresses
- Privacy Mode - User-controlled privacy settings
- Admin Dashboard - User management with filtering and search
- User Reporting System - Community-driven moderation
- Automatic Restrictions - Users with excessive reports get sharing restricted
- Centralized Error Logging - All exceptions logged to database
- Request Validation - Comprehensive DTO validation with class-validator
| Technology | Version | Purpose |
|---|---|---|
| NestJS | 10.x | Progressive Node.js framework for building scalable server-side applications |
| TypeScript | 5.x | Type-safe JavaScript superset |
| Express | 4.x | Underlying HTTP server (via @nestjs/platform-express) |
| Technology | Version | Purpose |
|---|---|---|
| MongoDB | 5.x+ | NoSQL document database |
| Mongoose | 8.x | MongoDB ODM for schema modeling and validation |
| @nestjs/mongoose | 11.x | NestJS MongoDB integration |
| Technology | Version | Purpose |
|---|---|---|
| @nestjs/jwt | 11.x | JWT token generation and verification |
| bcrypt | 5.x | Password hashing |
| ethers | 6.x | EVM wallet signature verification |
| Node.js Crypto | Built-in | AES-256-CBC encryption for secrets |
| Technology | Version | Purpose |
|---|---|---|
| telegram | 2.x | Telegram client library (GramJS) |
| nestjs-telegram-bot-api | 1.x | Telegram Bot API integration |
| Technology | Version | Purpose |
|---|---|---|
| class-validator | 0.14.x | Decorator-based validation |
| class-transformer | 0.5.x | Object transformation and serialization |
| Technology | Version | Purpose |
|---|---|---|
| @nestjs/axios | 4.x | HTTP client for external API calls |
| axios | 1.x | Promise-based HTTP client |
| Technology | Version | Purpose |
|---|---|---|
| Jest | 29.x | Testing framework |
| Supertest | 7.x | HTTP assertion library for e2e tests |
| ESLint | 8.x | Code linting |
| Prettier | 3.x | Code formatting |
| Technology | Version | Purpose |
|---|---|---|
| @vendia/serverless-express | 4.x | Serverless deployment adapter |
TACoSec employs military-grade encryption to protect your secrets. Here's a deep dive into how the encryption works and why it's secure.
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β AES-256-CBC ENCRYPTION FLOW β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ€
β β
β ββββββββββββββββ ββββββββββββββββ ββββββββββββββββββββββββββββββββ β
β β Plaintext β β Random IV β β 256-bit Secret Key β β
β β (Secret) β β (16 bytes) β β (32 bytes hex) β β
β ββββββββ¬ββββββββ ββββββββ¬ββββββββ ββββββββββββββββ¬ββββββββββββββββ β
β β β β β
β β β β β
β βΌ βΌ βΌ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β β
β β AES-256-CBC CIPHER β β
β β β β
β β Block 1: IV β Plaintext[0:16] β AES β Ciphertext[0:16] β β
β β Block 2: Ciphertext[0:16] β Plaintext[16:32] β AES β ... β β
β β Block N: Ciphertext[N-1] β Plaintext[N] β AES β Ciphertext[N] β β
β β β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β β
β βΌ β
β βββββββββββββββββββββββββββββββββββββ β
β β Encrypted Output Format β β
β β IV (hex) : Ciphertext (hex) β β
β β "a1b2c3...f0" : "e4d5f6...9a" β β
β βββββββββββββββββββββββββββββββββββββ β
β β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
| Security Aspect | Implementation | Protection Level |
|---|---|---|
| Key Size | 256 bits (32 bytes) | 2Β²β΅βΆ possible combinations - computationally infeasible to brute force |
| Algorithm | AES (Rijndael) | NIST-approved, used by US government for TOP SECRET data |
| Mode | CBC (Cipher Block Chaining) | Each block depends on previous block, preventing pattern analysis |
| IV Generation | Cryptographically random 16 bytes | Unique per encryption, prevents identical plaintext detection |
# Generate a cryptographically secure 256-bit key
npx ts-node src/utils/generate-key.ts
# Output: 64-character hex string (32 bytes)
# Example: c558ad827f514a3bc6fe872b2527890f6ed7f75febd5b7110e35af76424839acThe key is generated using Node.js crypto.randomBytes(32), which uses the operating system's cryptographically secure pseudorandom number generator (CSPRNG).
// For each secret, a unique random IV is generated
const iv = crypto.randomBytes(16); // 128-bit IV
// AES-256-CBC cipher is created with key and IV
const cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
// Plaintext is encrypted
let encrypted = cipher.update(plaintext, 'utf8', 'hex');
encrypted += cipher.final('hex');
// Output format: IV:Ciphertext
// Example: "a1b2c3d4e5f6a1b2c3d4e5f6a1b2c3d4:e4d5f6..."// Extract IV and ciphertext from stored value
const [ivHex, ciphertext] = encryptedData.split(':');
const iv = Buffer.from(ivHex, 'hex');
// Create decipher with same key and extracted IV
const decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
// Decrypt
let decrypted = decipher.update(ciphertext, 'hex', 'utf8');
decrypted += decipher.final('utf8');- 256-bit encryption ensures secrets cannot be read without the key
- Even with the fastest supercomputers, brute-forcing would take longer than the age of the universe
- Unique IV per encryption means encrypting the same password twice produces different ciphertexts.
- Attackers cannot determine if two users have the same password.
- CBC mode's block chaining ensures any modification to ciphertext corrupts decryption
- Altered data fails to decrypt properly, alerting to tampering.
- Encryption key is never stored in code - only in environment variables
- Key is loaded into memory only at runtime
- Server-side encryption means client devices never see the raw key
# β
DO: Generate a new key for production
npx ts-node src/utils/generate-key.ts
# β
DO: Store in environment variable
ENCRYPTION_KEY=your-64-char-hex-string
# β
DO: Use different keys for different environments
# Production, staging, and development should have separate keys
# β DON'T: Commit keys to version control
# β DON'T: Share keys via insecure channels
# β DON'T: Reuse keys across unrelated systems| Method | Key Size | Security Level | TACoSec Uses |
|---|---|---|---|
| AES-128 | 128 bits | Strong | β |
| AES-256-CBC | 256 bits | Military-grade | β |
| AES-256-GCM | 256 bits | Military-grade + Auth | Future consideration |
| RSA-2048 | 2048 bits | Strong (asymmetric) | For signatures only |
| ChaCha20 | 256 bits | Strong | β |
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Client Layer β
β (Telegram WebApp / Web Browser / Mobile App / Wallet) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β API Gateway Layer β
β βββββββββββββββ ββββββββββββββββ βββββββββββββββββββββββ β
β β JWT Guard β β Telegram β β Flexible Auth Guard β β
β β β β Auth Guard β β (JWT + Telegram) β β
β βββββββββββββββ ββββββββββββββββ βββββββββββββββββββββββ β
β ββββββββββββββββ β
β β Roles Guard β β
β β (Admin/User) β β
β ββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Controller Layer β
β ββββββββββ βββββββββ βββββββββββββ ββββββββββββββββββββββββββ β
β β Auth β β Users β β Passwords β β Public Addresses β β
β ββββββββββ βββββββββ βββββββββββββ ββββββββββββββββββββββββββ β
β βββββββββββββββββββ βββββββββββ ββββββββββ βββββββββββββββββ β
β β Notifications β β Reports β β Logger β β Telegram β β
β βββββββββββββββββββ βββββββββββ ββββββββββ βββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Service Layer β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β AuthContextService (Centralized Auth) β β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β ββββββββββββββββ ββββββββββββββββββ ββββββββββββββββββββββ β
β β UsersService β β PasswordServiceβ β NotificationsServiceβ β
β ββββββββββββββββ ββββββββββββββββββ ββββββββββββββββββββββ β
β ββββββββββββββββββββ ββββββββββββββββββββ β
β β TelegramValidatorβ β CryptoUtil β β
β ββββββββββββββββββββ ββββββββββββββββββββ β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β Data Layer β
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β β MongoDB (Mongoose) ββ
β β βββββββββ ββββββββββββ βββββββββββββββββ ββββββββββββββ ββ
β β β Users β β Passwordsβ β PublicAddressesβ β Notificationsβ ββ
β β βββββββββ ββββββββββββ βββββββββββββββββ ββββββββββββββ ββ
β β βββββββββββ βββββββββββββ ββββββββββββββ ββ
β β β Reports β β ErrorLogs β β Challenges β ββ
β β βββββββββββ βββββββββββββ ββββββββββββββ ββ
β βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
- Request Reception β Express receives HTTP request
- Global Pipes β ValidationPipe validates and transforms DTOs
- Guard Execution β Auth guards verify JWT/Telegram credentials
- Controller Handling β Route handler processes request
- Service Logic β Business logic execution
- Database Operations β Mongoose queries MongoDB
- Response Transformation β Data serialization
- Exception Handling β Global filter logs errors to database
- Node.js 18.x or higher
- npm 9.x or higher (or yarn)
- MongoDB 5.x or higher (local or cloud instance)
- Telegram Bot Token (for production Telegram auth validation)
-
Clone the repository
git clone <repository-url> cd tacosec-backend
-
Install dependencies
npm install
-
Generate encryption key
npx ts-node src/utils/generate-key.ts
Copy the generated key for the
.envfile.
Create a .env file in the project root:
# ===================
# Required Variables
# ===================
# MongoDB connection string
MONGODB_URI=mongodb://localhost:27017/tacosec
# JWT secret for token signing (use a strong random string)
JWT_SECRET=your-super-secret-jwt-key-min-32-chars
# Encryption key for password storage (32-byte hex string)
# Generate with: npx ts-node src/utils/generate-key.ts
ENCRYPTION_KEY=your-generated-encryption-key
# ===================
# Optional Variables
# ===================
# Environment
NODE_ENV=development
# JWT Token Expiration
JWT_EXPIRES_IN=24h
JWT_ACCESS_TOKEN_EXPIRES_IN=15m
JWT_REFRESH_TOKEN_EXPIRES_IN=7d
# ===================
# Telegram Configuration
# ===================
# Required in production for init data validation
TELEGRAM_BOT_TOKEN=your-telegram-bot-token
TELEGRAM_BOT_URL=https://t.me/your_bot
# Admin identification
ADMIN_TELEGRAM_ID=123456789
# Telegram Client (for contacts lookup features)
TELEGRAM_API_ID=your-api-id
TELEGRAM_API_HASH=your-api-hash
TELEGRAM_SESSION_PATH=./sessions
TELEGRAM_REQUEST_TIMEOUT=30000
TELEGRAM_MAX_RETRIES=3
TELEGRAM_RETRY_DELAY=1000
TELEGRAM_DEBUG=false
TELEGRAM_CACHE_TTL=300
TELEGRAM_MAX_CONTACTS_PER_REQUEST=100
# ===================
# Reports Configuration
# ===================
# Number of reports before automatic restriction
MAX_REPORTS_BEFORE_BAN=10
# Percentage threshold for restriction
MAX_PERCENTAGE_OF_REPORTS_REQUIRED_FOR_BAN=0.5
# ===================
# Feature Flags
# ===================
IS_STAGING=true# Development mode (with hot reload)
npm run start:dev
# Debug mode (with inspector)
npm run start:debug
# Production mode (requires build first)
npm run build
npm run start:prodThe server starts on http://localhost:3000 in development mode.
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/auth/challange |
Create wallet auth challenge | None |
POST |
/auth/login |
Login with Telegram or wallet | Telegram Header |
POST |
/auth/refresh |
Refresh access token | None |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/users/signup |
Register via Telegram DTO | Telegram |
POST |
/users/signup-initData |
Register via init data header | Telegram |
GET |
/users/me |
Get current user info | Flexible |
PATCH |
/users/update-info |
Update user profile | Flexible |
GET |
/users/search/autocomplete |
Search users by username | Flexible |
PATCH |
/users/me/privacy-mode |
Toggle privacy mode | Flexible |
GET |
/users/admin/all |
List all users (admin) | Admin |
PATCH |
/users/admin/active-status/:id |
Toggle user status (admin) | Admin |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/passwords |
Create new secret | Telegram DTO |
GET |
/passwords |
Get user's secrets | Telegram DTO |
PATCH |
/passwords/:id |
Update secret | Telegram DTO |
DELETE |
/passwords/:id |
Delete secret | Telegram DTO |
GET |
/passwords/shared-with-me |
Get secrets shared with user | Telegram DTO |
PATCH |
/passwords/secret-view/:id |
Record secret view | Flexible |
GET |
/passwords/children/:parentId |
Get child secrets | Flexible |
GET |
/passwords/admin/all |
List all secrets (admin) | Admin |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/public-addresses |
Link wallet address | Flexible |
POST |
/public-addresses/challange |
Create signing challenge | Flexible |
GET |
/public-addresses |
Get user's addresses | Flexible |
GET |
/public-addresses/:userId |
Get addresses by user ID | Flexible |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
GET |
/notifications/my |
Get current user's notifications | Flexible |
GET |
/notifications |
List all notifications (admin) | Admin |
GET |
/notifications/stats |
Get notification stats (admin) | Admin |
DELETE |
/notifications/cleanup/:days |
Clean old notifications (admin) | Admin |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/reports |
Report a user | Flexible |
GET |
/reports/is-restricted/:userIdentifier |
Check if user is restricted | Flexible |
GET |
/reports/admin/reported-users |
List reported users (admin) | Admin |
PATCH |
/reports/admin/resolve/:id |
Resolve a report (admin) | Admin |
| Method | Endpoint | Description | Auth |
|---|---|---|---|
POST |
/logger |
Save error log | Flexible |
GET |
/logger/:id |
Get log by ID | Flexible |
DELETE |
/logger/:id |
Delete log | Flexible |
GET |
/logger |
List all logs (admin) | Admin |
GET |
/logger/admin/all |
List logs with filters (admin) | Admin |
Send Telegram WebApp init data in the request header:
POST /users/signup HTTP/1.1
x-telegram-init-data: query_id=AAHdF6IQAAAAAN0XohDhrOrc&user=%7B%22id%22%3A123456789%2C%22first_name%22%3A%22John%22%7D&auth_date=1619493727&hash=fa92cf66...The server validates the hash against TELEGRAM_BOT_TOKEN to verify authenticity.
After login, use the access token in subsequent requests:
GET /users/me HTTP/1.1
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...For wallet-based authentication:
-
Get Challenge
POST /auth/challange Content-Type: application/json { "publicAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f..." }
-
Sign the challenge with your wallet
-
Login with signature
POST /auth/login Content-Type: application/json { "publicAddress": "0x742d35Cc6634C0532925a3b844Bc9e7595f...", "signature": "0x..." }
The signature is verified using ethers.verifyMessage().
Many endpoints accept either authentication method. The system prioritizes JWT if both are provided:
Priority: JWT Bearer Token > Telegram Init Data Header
# Run unit tests
npm run test
# Run tests in watch mode
npm run test:watch
# Run specific test file
npm run test -- src/users/users.service.spec.ts
# Run e2e tests
npm run test:e2e
# Run integration tests
npm run test:integration
# Generate coverage report
npm run test:cov- Unit tests:
jest.config.js - E2E tests:
test/jest-e2e.json - Test encryption key is automatically set via
cross-env
The application exports a serverless-compatible handler for platforms like Vercel, AWS Lambda, etc:
// src/main.ts
export const handler = async (request, response) => {
const app = await bootstrap();
const httpAdapter = app.getHttpAdapter();
return httpAdapter.getInstance()(request, response);
};# lint (auto-fix enabled)
yarn run lint
# build
yarn run build
# start (dev watch)
yarn run start:dev
# start (non-watch)
yarn run start
# start production (run compiled dist)
yarn run start:prod
# unit tests
yarn run test
# e2e tests
yarn run test:e2e
# integration tests (selected)
yarn run test:integration
# format code with Prettier
yarn run format
# Vercel build (CI)
yarn run vercel-buildThis is a high-level overview and not a complete API reference.
POST /auth/login- Telegram-only login: provide
x-telegram-init-dataheader and an empty body. - Wallet login: provide
{ "publicAddress": "0x...", "signature": "..." }. - Wallet signature is verified only when
publicAddressis provided.
- Telegram-only login: provide
POST /public-addresses(flexible auth)- Body:
{ "publicKey": "0x...", "signature": "...", "secret"?: "..." } - Signature is required and must match the provided
publicKey.
- Body:
GET /public-addresses(flexible auth)- Returns linked addresses for the authenticated user.
POST /reports(flexible auth)GET /reports/is-restricted/:userIdentifier(flexible auth)GET /reports/admin/reported-users(admin)GET /reports/admin/user/:userIdentifier(admin)PATCH /reports/admin/resolve/:id(admin)
- Generate encryption key:
# Generate new encryption key
npx ts-node src/utils/generate-key.ts
# Cleanup invalid public addresses (maintenance)
npx ts-node src/scripts/cleanup-null-keys.tsThis project is UNLICENSED - proprietary software.
ποΈ Nest.js β Progressive Node.js framework
ποΈ MongoDB β Document database for encrypted data
π Telegram Bot API β Authentication for Telegram Mini App users
π ethers.js β Wallet address authentication
- Web Users: Validated via seed phrase wallet signatures.
- Telegram Access: Telegram Mini App & Bot API authentication + seed phrase wallet signatures for the data access.
TACoSec β’ Frontend β’ Backend (you are here) β’ Powered by TACo π β’ Built with β€οΈ by Web3Web4