{"id":16679,"library":"remix-auth-totp","title":"Remix Auth TOTP","description":"remix-auth-totp is an authentication strategy for Remix Auth that enables Time-Based One-Time Password (TOTP) based authentication, including support for email-code and magic link workflows. The current stable version is 4.0.0, which introduced compatibility with Remix Auth v4 and React Router v7. The package maintains a regular release cadence, with major versions often aligning with upstream Remix Auth updates, and minor/patch releases addressing features and bug fixes. Key differentiators include built-in magic link functionality, support for Cloudflare Pages, enhanced security through JWE encryption and SHA256 hashing (since v3.2.0), and a strong TypeScript foundation. A significant architectural shift in v3.0.0 eliminated direct database reliance, simplifying its integration model.","status":"active","version":"4.0.0","language":"javascript","source_language":"en","source_url":"https://github.com/dev-xo/remix-auth-totp","tags":["javascript","remix","remix-run","remix-auth","remix-auth-totp","authentication","totp","typescript"],"install":[{"cmd":"npm install remix-auth-totp","lang":"bash","label":"npm"},{"cmd":"yarn add remix-auth-totp","lang":"bash","label":"yarn"},{"cmd":"pnpm add remix-auth-totp","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Core authentication library that remix-auth-totp extends.","package":"remix-auth","optional":false}],"imports":[{"note":"remix-auth-totp primarily uses named exports. Direct CommonJS require without destructuring will not work.","wrong":"const TOTPStrategy = require('remix-auth-totp');","symbol":"TOTPStrategy","correct":"import { TOTPStrategy } from 'remix-auth-totp';"},{"note":"This is a TypeScript type interface; use `import type` for clarity and better tree-shaking where supported.","wrong":"import { TOTPStrategyOptions } from 'remix-auth-totp';","symbol":"TOTPStrategyOptions","correct":"import type { TOTPStrategyOptions } from 'remix-auth-totp';"},{"note":"Use `TOTPError` for catching specific errors thrown by the strategy during authentication.","wrong":"const { TOTPError } = require('remix-auth-totp');","symbol":"TOTPError","correct":"import { TOTPError } from 'remix-auth-totp';"}],"quickstart":{"code":"import { Authenticator } from \"remix-auth\";\nimport { TOTPStrategy } from \"remix-auth-totp\";\nimport { createCookieSessionStorage, redirect } from \"@remix-run/node\";\n\ninterface User {\n  id: string;\n  email: string;\n}\n\n// 1. Setup session storage\nconst sessionStorage = createCookieSessionStorage({\n  cookie: {\n    name: \"__session\",\n    httpOnly: true,\n    sameSite: \"lax\",\n    path: \"/\",\n    secrets: [process.env.SESSION_SECRET ?? 'a_secret_key'], // Replace with actual secret\n    secure: process.env.NODE_ENV === \"production\",\n  },\n});\n\n// 2. Initialize Authenticator\nexport const authenticator = new Authenticator<User>(sessionStorage);\n\n// Mock database for TOTP secrets (replace with your actual database)\nconst userTotpSecrets = new Map<string, string>();\nuserTotpSecrets.set(\"test@example.com\", process.env.TOTP_USER_SECRET ?? 'A_VERY_SECURE_SECRET');\n\n// 3. Register the TOTP Strategy\nauthenticator.use(\n  new TOTPStrategy(\n    {\n      secret: async ({ email }) => {\n        // Fetch the user's TOTP secret from your database\n        const secret = userTotpSecrets.get(email);\n        if (!secret) {\n          throw new Error(`TOTP not configured for ${email}.`);\n        }\n        return secret;\n      },\n      sendTOTP: async ({ email, code, magicLink, request }) => {\n        // In a real app, send `code` or `magicLink` via email/SMS to `email`.\n        console.log(`Sending code ${code} or magic link to ${email}`);\n        // Example: await sendEmail({ to: email, subject: \"Your TOTP Code\", body: `Code: ${code}. Or login: ${magicLink}` });\n      },\n      // You can add other options like `maxAge`, `issuer`, `magicLinkPath`\n    },\n    async ({ email, code, magicLink }) => {\n      // Verify the user and return the user object upon successful authentication.\n      // This is where you would fetch the user from your DB and return it.\n      if (email === \"test@example.com\" && (code === \"123456\" || magicLink)) { // Simplified logic\n        return { id: \"user-abc\", email: \"test@example.com\" };\n      }\n      throw new Error(\"Invalid TOTP or Magic Link.\");\n    }\n  ),\n  \"totp\" // Unique name for this strategy\n);\n\n// 4. Example Remix Action (e.g., in `app/routes/login.tsx`)\nexport async function action({ request }: { request: Request }) {\n  try {\n    return await authenticator.authenticate(\"totp\", request, {\n      successRedirect: \"/dashboard\",\n      failureRedirect: \"/login?error=true\",\n    });\n  } catch (error) {\n    if (error instanceof Response && error.status >= 300 && error.status < 400) {\n      throw error; // Propagate redirects from authenticator (e.g., magic link sent)\n    }\n    console.error(\"Login failed:\", error);\n    return redirect(\"/login?error=true\");\n  }\n}","lang":"typescript","description":"Demonstrates setting up `remix-auth-totp` with `remix-auth` to initialize the strategy with `secret` and `sendTOTP` callbacks, and handle an authentication attempt in a Remix action."},"warnings":[{"fix":"Ensure `remix-auth` is installed at `^4.1.0` or higher when using `remix-auth-totp@4.x.x`.","message":"Version 4.0.0 introduces support for Remix Auth v4. Applications using older versions of Remix Auth (v3 or earlier) must upgrade `remix-auth` to a compatible v4 release when updating `remix-auth-totp` to v4.0.0.","severity":"breaking","affected_versions":"^4.0.0"},{"fix":"Review the v3.0.0 changelog and documentation; adapt your application to manage TOTP secrets and state externally, providing them via the `secret` callback.","message":"Version 3.0.0 fundamentally changed the strategy's internal workings by eliminating direct reliance on a database for state management. This means `TOTPData` and CRUD interfaces introduced in v2 are no longer used. Existing implementations from v2 will require significant refactoring to adapt to the new stateless approach.","severity":"breaking","affected_versions":">=3.0.0"},{"fix":"Thoroughly test existing TOTP setups after upgrading. You may need to have users re-register their TOTP devices or ensure compatibility with the new hashing algorithms.","message":"Version 3.2.0 enhanced security by introducing JWE encryption and updating the default hashing algorithm to SHA256. This change might invalidate existing TOTP secrets or verification flows if not properly migrated, as the underlying cryptographic methods have changed.","severity":"breaking","affected_versions":">=3.2.0"},{"fix":"Upgrade to `remix-auth-totp` v3.4.2 or higher to ensure full compatibility with Node.js v20 and newer runtime environments.","message":"Versions prior to 3.4.2 might encounter issues when running on Node.js v20+ environments due to changes in Node.js's `Buffer` and `crypto` APIs. Specifically, `Buffer` was replaced with `Uint8Array` and `crypto` with Web Crypto.","severity":"gotcha","affected_versions":"<3.4.2"},{"fix":"Upgrade to `remix-auth-totp` v1.4.1 or higher, which implements a fix to use the request's origin instead of headers when `hostUrl` is not provided.","message":"Magic link generation in environments like Cloudflare local development (wrangler/miniflare) using versions prior to 1.4.1 might use incorrect host URLs, leading to broken magic links.","severity":"gotcha","affected_versions":"<1.4.1"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Upgrade `remix-auth-totp` to v3.4.2 or higher to utilize `Uint8Array` and Web Crypto API for compatibility with modern Node.js versions.","cause":"Running an older version of `remix-auth-totp` (prior to v3.4.2) on Node.js v20 or a similar runtime environment that has removed the global Buffer constructor.","error":"TypeError: Buffer is not a constructor"},{"fix":"Ensure the `TOTPStrategy` constructor is provided with a `secret` async function that reliably retrieves the user's unique TOTP secret based on the input (e.g., email).","cause":"The `secret` callback function was not provided or returned null/undefined in the `TOTPStrategy` constructor options.","error":"Error: TOTPStrategy: secret is a required option"},{"fix":"Use a named import: `import { TOTPStrategy } from 'remix-auth-totp';` or for CommonJS: `const { TOTPStrategy } = require('remix-auth-totp');`.","cause":"Incorrect CommonJS `require` syntax or incorrect named import for `TOTPStrategy` in an ES module context.","error":"ReferenceError: TOTPStrategy is not defined"},{"fix":"Debug the `verify` callback. Check if the submitted `code` matches the expected TOTP, if the `magicLink` is valid and not expired, and if the user data used for verification is correct.","cause":"The `verify` callback function provided to the `TOTPStrategy` threw an error, indicating that the submitted code or magic link was incorrect, expired, or the associated user could not be found.","error":"Authentication failed: Error: Invalid TOTP code or Magic Link."}],"ecosystem":"npm"}