{"id":31532610,"url":"https://github.com/ssbit01/secure-otp-server","last_synced_at":"2026-04-10T07:38:51.114Z","repository":{"id":317866195,"uuid":"1065471873","full_name":"SSbit01/secure-otp-server","owner":"SSbit01","description":"A template server for generating, encrypting, and verifying One-Time Passwords (OTP). Designed for microservices, modern authentication flows, and serverless environments.","archived":false,"fork":false,"pushed_at":"2025-10-03T15:34:21.000Z","size":220,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2025-10-03T15:38:55.779Z","etag":null,"topics":["bun","cloudflare","cookie","decrypt","deno","dockerfile","encrypt","generation","hono","http","javascript","key","otp","secure","server","serverless","template","typescript","verification","wrangler"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SSbit01.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-27T19:53:17.000Z","updated_at":"2025-10-03T15:34:26.000Z","dependencies_parsed_at":null,"dependency_job_id":"7dd4610f-1359-4635-bdaa-55a09df39a72","html_url":"https://github.com/SSbit01/secure-otp-server","commit_stats":null,"previous_names":["ssbit01/secure-otp-server"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/SSbit01/secure-otp-server","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SSbit01%2Fsecure-otp-server","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SSbit01%2Fsecure-otp-server/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SSbit01%2Fsecure-otp-server/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SSbit01%2Fsecure-otp-server/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SSbit01","download_url":"https://codeload.github.com/SSbit01/secure-otp-server/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SSbit01%2Fsecure-otp-server/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":278262444,"owners_count":25957938,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-04T02:00:05.491Z","response_time":63,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["bun","cloudflare","cookie","decrypt","deno","dockerfile","encrypt","generation","hono","http","javascript","key","otp","secure","server","serverless","template","typescript","verification","wrangler"],"created_at":"2025-10-04T03:56:40.029Z","updated_at":"2026-04-10T07:38:51.106Z","avatar_url":"https://github.com/SSbit01.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Secure OTP Server\n\n![Logo](/logo.png \"Secure OTP Server\")\n\nA template server for generating, encrypting, and verifying **One-Time Passwords** (OTP).\nPerfect for **passwordless authentication systems**, **credential verification** (email, phone number, etc.), and modern **MFA flows**.\nDesigned for microservices, high security environments, and both traditional or serverless deployments.\n\n\u003e [!CAUTION]\n\u003e\n\u003e This server implements several security best practices, but it is not a complete security solution on its own.\n\u003e Additional measures such as DDoS protection, rate limiting, and request throttling are necessary for a production environment.\n\u003e It is recommended to configure these externally via a reverse proxy.\n\u003e\n\u003e If you discover a vulnerability, please read the [Security Policy](./SECURITY.md).\n\n## Use Cases\n\n- **Passwordless Authentication**: Use OTPs as the primary login method, eliminating the need for passwords.\n- **Credential Verification**: Verify ownership of an email address or phone number during registration or profile updates.\n- **Multi-Factor Authentication (MFA)**: Add an extra layer of security to traditional login flows.\n- **Secure Actions**: Protect sensitive operations (e.g. password resets, high-value transactions...) with a temporary verification code.\n\n## Features\n\n### Secure by Design\n\nGenerates cryptographically secure OTPs and encrypts session data using envelope encryption with\n[AES-256-KW](https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/wrapKey#AES-256-KW) (KEK)\nand [AES-256-GCM](https://en.wikipedia.org/wiki/Galois/Counter_Mode) (DEK),\nwhich is extremely fast on modern CPUs because they have dedicated hardware acceleration\n([AES-NI](https://en.wikipedia.org/wiki/AES_instruction_set)),\nin addition to being quantum-resistant.\n\n### Customizable\n\nEasily adapt logic for OTP generation, credential validation, and OTP delivery (e.g. email, SMS...).\n\n### State Aware\n\nPrevents replay attacks by using single use verification keys, while remaining lightweight.\n\n### Multi-Credential Sessions\n\nStore several OTP tokens per session, each bound to a different credential.\nUsers can move between credentials without restarting the flow,\nand the session encrypted cookie enforces a strict cap so tokens stay lightweight.\n\n### High Performance\n\nBuilt with [Hono](https://hono.dev/) for fast and efficient routing.\n\n### Deployment Flexibility\n\nSupports both traditional server environments (using the included [Dockerfile](/Dockerfile) that leverages [Bun](https://bun.com/))\nand serverless platforms.\n\nIt works out of the box with a built-in in-memory storage, and can be easily configured to use external stores\n(needed for distributed or serverless setups). See [Customization](#customization).\n\n### Web Standards Based\n\nRuns on modern JavaScript runtimes ([Deno](https://deno.com/), [Bun](https://bun.com/),\n[Cloudflare Workers](https://workers.cloudflare.com/)...).\n\nYou can run it on [Node.js](https://nodejs.org/) (\u003e=25), though [Deno](https://deno.com/) and especially [Bun](https://bun.com/)\nare more recommended.\nTo use [Node.js](https://nodejs.org/), install the\n[`@hono/node-server`](https://github.com/honojs/node-server?tab=readme-ov-file#usage)\nadapter and configure it in [`src/index.ts`](/src/index.ts).\n\nFor additional deployment targets such as [Fastly Compute](https://www.fastly.com/products/edge-compute) or\n[AWS Lambda](https://aws.amazon.com/lambda), refer to the\n[Hono documentation](https://hono.dev/docs/getting-started/basic#next-step).\n\n## Architecture\n\nThis server uses a hybrid design that provides even more security than a stateful design.\nThe server only stores random IDs, so it cannot know which credentials are currently being verified,\nproviding enhanced privacy and security without the overhead of a traditional storage system.\n\n1. When an OTP is created, its metadata (credential, expiry, attempts...) is compressed and appended to an encrypted list of tokens\n(one entry per credential) using envelope encryption with AES-256-KW (KEK) and AES-256-GCM (DEK).\nThe encrypted list is sent to the client in a secure, `HttpOnly` cookie.\n2. A random ID linked to the list is generated and stored on the server.\n3. When the client attempts to verify an OTP token, it sends back the encrypted list.\nThe server selects the current credential's token, and after each verification attempt updates its ID.\n4. The encrypted cookie stores at most `OTP_MAX_CREDENTIALS` entries, so users can switch between\nmultiple credentials without restarting the flow while keeping the session footprint small.\n\nThis process ensures that each encrypted token can only be used for verification once, effectively preventing replay attacks.\nBy default, the KMS stores key encryption keys (KEKs) in memory, but it can be customized in\n[`src/custom/kms.ts`](/src/custom/kms.ts) to use a persistent store like Redis or KV storage for serverless environments\nor distributed systems.\n\n## Getting Started\n\n### 1. Installation\n\nClone the repository and install dependencies using your preferred package manager.\n\n```sh\n# Using Bun\nbun install\n\n# Using Deno\ndeno task install\n```\n\n### 2. Configuration\n\nCreate a `.env` file in the root of the project. For production, set `NODE_ENV` to `\"production\"` to enable secure cookies\nand specify your frontend's origin with `CORS_ORIGIN`.\n\n```env\n# .env\nNODE_ENV=\"production\"\nCORS_ORIGIN=\"https://your-app.com\"\n```\n\nSee [`sample.env`](/sample.env).\n\n### 3. Running the Server\n\nYou can run the server in development mode.\n\n```sh\n# Using Bun\nbun run bun:dev\n\n# Using Deno\ndeno task deno:dev\n```\n\n## API Reference\n\nFor a complete specification, see the [`openapi.json`](/openapi.json) file.\n\n### `POST /api/otp/create`\n\nGenerates a new OTP, encrypts the session data, and sends it to the user.\nThis endpoint sets one `HttpOnly` cookie that must be included in subsequent requests.\n\n- **Body**: `application/json`. The schema is defined in [`src/custom/credential.ts`](/src/custom/credential.ts).\n- **Logic**: The OTP sending logic is defined in [`src/custom/send.ts`](/src/custom/send.ts).\n- **Multi-credential flow**: Sending this request again with a different credential adds another OTP token\n(until `OTP_MAX_CREDENTIALS` is reached).\n\n### `POST /api/otp/resend`\n\nGenerates and sends a new OTP for the current token. This endpoint does not require a request body.\n\n- **Logic**: Resend timing and limits can be configured in [`src/custom/otp.ts`](/src/custom/otp.ts).\n\n### `POST /api/otp/verify`\n\nVerifies an OTP code.\n\n- **Body**: `application/x-www-form-urlencoded` with an `otp` parameter (e.g. `otp=12345678`).\n- **Logic**: After successful verification, a final action is triggered, defined in [`src/custom/final.ts`](/src/custom/final.ts).\n\n## Customization\n\nKey logic is separated into the following modules:\n\n- [`src/custom/otp.ts`](/src/custom/otp.ts): OTP generation logic (length, characters, expiry), resend delays,\nand the `OTP_MAX_CREDENTIALS` cap that governs multi-credential sessions.\n- [`src/custom/credential.ts`](/src/custom/credential.ts): Validation schema for the `/api/otp/create` request body.\n- [`src/custom/send.ts`](/src/custom/send.ts): Logic for sending the OTP to the user (e.g. using an email or SMS service).\n- [`src/custom/id.ts`](/src/custom/id.ts): Storage for OTP token list IDs (defaults to in-memory).\n- [`src/custom/kms.ts`](/src/custom/kms.ts): Storage for encryption key encryptions keys (KEKs; defaults to in-memory).\n- [`src/custom/final.ts`](/src/custom/final.ts): Action to perform after successful OTP verification.\n\nEach file contains detailed comments explaining how to modify the code.\n\n### Advanced\n\nYou can configure the server's middleware and CORS behavior in [`src/setup/index.ts`](/src/setup/index.ts).\n\nDefault settings include:\n\n- All other requests return a 404 error with an empty response body.\n- Request body size is capped at 100 KiB.\n\nThese defaults are fully customizable within the same file.\n\n## Errors\n\nServer error responses follow this structure:\n\n```typescript\n{\n  error: string;\n  message?: string;\n}\n```\n\n- `error`: A string representing the error code. You can find all error codes in [`src/lib/error/names.js`](/src/lib/error/names.js).\n- `message`: A human readable description of the error. Static error messages can be changed in\n[`src/lib/error/static.js`](src/lib/error/static.js).\n\n## Testing\n\nThe test suite is written with Bun's built-in test runner. Use `bun run bun:test` to run the tests.\n\n## License\n\nThis project is [MIT licensed](/LICENSE).\n\nThe default NPM build scripts automatically use the [generate-license-file](https://www.npmjs.com/package/generate-license-file)\nCLI to bundle all dependency licenses with your build, ensuring effortless compliance.\n\n---\n\nOriginally created by [SSbit01](https://ssbit01.github.io).","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssbit01%2Fsecure-otp-server","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fssbit01%2Fsecure-otp-server","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fssbit01%2Fsecure-otp-server/lists"}