{"id":16743,"library":"remix-auth-email-link","title":"Remix Auth Email Link Strategy","description":"remix-auth-email-link provides a passwordless authentication strategy for Remix applications, leveraging 'magic links' sent via email. It is heavily inspired by the kcd strategy from Remix Auth v2. The library is actively maintained, with frequent patch and minor releases, and its current stable version is 2.1.1. A significant differentiator is its use of `crypto-js` instead of Node's built-in `crypto` module, enabling deployment on various serverless runtimes like Cloudflare Workers. It integrates with the `remix-auth` ecosystem and requires a custom `sendEmail` function to interface with any email service (e.g., Mailgun, SendGrid, AWS SES). This strategy streamlines user login by eliminating password management, offering a user-friendly and secure authentication flow.","status":"active","version":"2.1.1","language":"javascript","source_language":"en","source_url":"https://github.com/pbteja1998/remix-auth-email-link","tags":["javascript","remix","remix-auth","auth","authentication","strategy","typescript"],"install":[{"cmd":"npm install remix-auth-email-link","lang":"bash","label":"npm"},{"cmd":"yarn add remix-auth-email-link","lang":"bash","label":"yarn"},{"cmd":"pnpm add remix-auth-email-link","lang":"bash","label":"pnpm"}],"dependencies":[{"reason":"Peer dependency for Remix application server utilities.","package":"@remix-run/server-runtime","optional":false},{"reason":"Core peer dependency for Remix authentication framework.","package":"remix-auth","optional":false}],"imports":[{"note":"Primary class for setting up email link authentication; recommended for ESM environments.","wrong":"const { EmailLinkStrategy } = require('remix-auth-email-link')","symbol":"EmailLinkStrategy","correct":"import { EmailLinkStrategy } from 'remix-auth-email-link'"},{"note":"TypeScript type definition for the email sending function required by the strategy. Use as a type import.","symbol":"SendEmailFunction","correct":"import type { SendEmailFunction } from 'remix-auth-email-link'"},{"note":"The `Authenticator` class is the core of Remix Auth and must be imported from the `remix-auth` package, not `remix-auth-email-link`.","wrong":"import { Authenticator } from 'remix-auth-email-link'","symbol":"Authenticator","correct":"import { Authenticator } from 'remix-auth'"}],"quickstart":{"code":"import { Authenticator } from 'remix-auth';\nimport { EmailLinkStrategy } from 'remix-auth-email-link';\nimport { createCookieSessionStorage, redirect } from '@remix-run/node';\n\n// Minimal session storage for demonstration\nconst sessionStorage = createCookieSessionStorage({\n  cookie: {\n    name: '__session',\n    httpOnly: true,\n    path: '/',\n    sameSite: 'lax',\n    secrets: [process.env.SESSION_SECRET ?? 's3cr3t'],\n    secure: process.env.NODE_ENV === 'production',\n  },\n});\n\n// Mock user model and email service for example\ninterface User { id: string; email: string; name: string; }\nconst users: User[] = [\n  { id: '1', email: 'user@example.com', name: 'Test User' }\n];\nconst getUserByEmail = (email: string) => users.find(u => u.email === email);\nconst createUser = (email: string) => {\n  const newUser = { id: String(users.length + 1), email, name: 'New User' };\n  users.push(newUser);\n  return newUser;\n};\n\nconst sendEmail = async ({ emailAddress, magicLink, user, domainUrl, form }) => {\n  console.log(`Sending magic link to ${emailAddress}: ${magicLink}`);\n  // In a real app, integrate with an actual email service like Mailgun/SendGrid/SES\n  // e.g., await emailProvider.sendEmail(emailAddress, 'Login Link', `<a href=\"${magicLink}\">Click to login</a>`);\n};\n\nconst secret = process.env.MAGIC_LINK_SECRET ?? 'another_s3cr3t';\nif (!secret) throw new Error('Missing MAGIC_LINK_SECRET env variable.');\n\nexport const authenticator = new Authenticator<User>(sessionStorage);\n\nauthenticator.use(\n  new EmailLinkStrategy(\n    { sendEmail, secret, callbackURL: '/magic' },\n    async ({ email, form, request, magicLink, user }) => {\n      let existingUser = getUserByEmail(email);\n\n      if (!existingUser) {\n        // Create user if they don't exist\n        existingUser = createUser(email);\n      }\n\n      // Log the user in\n      return existingUser;\n    }\n  ),\n  'email-link'\n);\n\n// Example Remix loader for a magic link callback route (/magic)\nexport const loader = async ({ request }) => {\n  return await authenticator.authenticate('email-link', request, {\n    successRedirect: '/dashboard',\n    failureRedirect: '/login',\n  });\n};\n\n// Example Remix action for a login form\nexport const action = async ({ request }) => {\n  return await authenticator.authenticate('email-link', request, {\n    successRedirect: '/login?checkEmail=true', // Redirect to a page asking user to check email\n    failureRedirect: '/login',\n  });\n};\n\nconsole.log('Authenticator and strategy initialized.');\n","lang":"typescript","description":"This quickstart demonstrates how to initialize the `EmailLinkStrategy` with `remix-auth`, including mock user management and email sending functions, and how to integrate it into Remix `loader` and `action` functions for login and magic link callbacks."},"warnings":[{"fix":"Ensure your `@remix-run/*` packages are at version `^2.0.0` or higher, and update `remix-auth-email-link` to `^2.0.0` or higher. Check your `package.json` and run `npm install` or `yarn install`.","message":"Version 2.0.0 introduced compatibility with Remix v2. If you are upgrading your Remix application to v2, you must also upgrade `remix-auth-email-link` to v2.x to maintain compatibility.","severity":"breaking","affected_versions":">=2.0.0"},{"fix":"Define `MAGIC_LINK_SECRET` in your environment variables (e.g., `.env` file for development, your hosting provider's configuration for production) with a long, random string. Example: `MAGIC_LINK_SECRET=\"your-super-secret-random-string\"`.","message":"The strategy relies on a `MAGIC_LINK_SECRET` environment variable for encrypting tokens. Failing to provide this secret will result in a runtime error during initialization.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Verify that the `callbackURL` string in your `EmailLinkStrategy` configuration (e.g., `/magic`) corresponds to the actual path of your Remix `loader` or `action` that handles the incoming magic link request.","message":"The `callbackURL` option provided to `EmailLinkStrategy` must exactly match the URL of the route where you call `authenticator.authenticate('email-link', request, { ... })` for processing the magic link.","severity":"gotcha","affected_versions":">=1.0.0"},{"fix":"Upgrade to `remix-auth-email-link@2.0.2` or newer, which ensures `http` is used as the protocol when running locally. Alternatively, ensure your local setup correctly handles `X-Forwarded-Proto` headers if using a proxy.","message":"When running locally, older versions of the package (prior to v2.0.2) might incorrectly assume `https` for magic links. This can cause issues in development environments where `http` is typically used.","severity":"gotcha","affected_versions":"<2.0.2"}],"env_vars":null,"last_verified":"2026-04-22T00:00:00.000Z","next_check":"2026-07-21T00:00:00.000Z","problems":[{"fix":"Set the `MAGIC_LINK_SECRET` environment variable in your development and production environments. For example, in a `.env` file: `MAGIC_LINK_SECRET=\"a_very_secure_random_string\"`.","cause":"The `MAGIC_LINK_SECRET` environment variable is not set, which is required by the `EmailLinkStrategy` for token encryption.","error":"Error: Missing MAGIC_LINK_SECRET env variable."},{"fix":"Ensure your Remix project and build setup are configured for ESM. If using TypeScript, check `tsconfig.json` for `\"module\": \"esnext\"` or `\"module\": \"node16\"`. If still encountering issues, verify that `remix.config.js` is correctly configured for your target environment (e.g., Cloudflare, Node).","cause":"This error typically occurs in CommonJS environments when trying to import an ESM-only package or when mixing import/require styles incorrectly, especially in older Remix setups or certain bundler configurations.","error":"TypeError: (0 , import_remix_auth_email_link.EmailLinkStrategy) is not a constructor"},{"fix":"Check the `successRedirect` and `failureRedirect` options passed to `authenticator.authenticate`. Ensure your `EmailLinkStrategy`'s `verify` callback correctly returns a user object or throws an error as expected. Also, confirm the `callbackURL` matches the actual route.","cause":"This error usually indicates that the `authenticator.authenticate` call in your loader/action did not result in a redirect or other handled response, often due to an issue within the strategy's verify callback or options.","error":"Error: Response not handled by strategy (e.g. redirect or body)."}],"ecosystem":"npm"}