Remix Auth Email Link Strategy

2.1.1 · active · verified Wed Apr 22

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.

Common errors

Warnings

Install

Imports

Quickstart

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.

import { Authenticator } from 'remix-auth';
import { EmailLinkStrategy } from 'remix-auth-email-link';
import { createCookieSessionStorage, redirect } from '@remix-run/node';

// Minimal session storage for demonstration
const sessionStorage = createCookieSessionStorage({
  cookie: {
    name: '__session',
    httpOnly: true,
    path: '/',
    sameSite: 'lax',
    secrets: [process.env.SESSION_SECRET ?? 's3cr3t'],
    secure: process.env.NODE_ENV === 'production',
  },
});

// Mock user model and email service for example
interface User { id: string; email: string; name: string; }
const users: User[] = [
  { id: '1', email: 'user@example.com', name: 'Test User' }
];
const getUserByEmail = (email: string) => users.find(u => u.email === email);
const createUser = (email: string) => {
  const newUser = { id: String(users.length + 1), email, name: 'New User' };
  users.push(newUser);
  return newUser;
};

const sendEmail = async ({ emailAddress, magicLink, user, domainUrl, form }) => {
  console.log(`Sending magic link to ${emailAddress}: ${magicLink}`);
  // In a real app, integrate with an actual email service like Mailgun/SendGrid/SES
  // e.g., await emailProvider.sendEmail(emailAddress, 'Login Link', `<a href="${magicLink}">Click to login</a>`);
};

const secret = process.env.MAGIC_LINK_SECRET ?? 'another_s3cr3t';
if (!secret) throw new Error('Missing MAGIC_LINK_SECRET env variable.');

export const authenticator = new Authenticator<User>(sessionStorage);

authenticator.use(
  new EmailLinkStrategy(
    { sendEmail, secret, callbackURL: '/magic' },
    async ({ email, form, request, magicLink, user }) => {
      let existingUser = getUserByEmail(email);

      if (!existingUser) {
        // Create user if they don't exist
        existingUser = createUser(email);
      }

      // Log the user in
      return existingUser;
    }
  ),
  'email-link'
);

// Example Remix loader for a magic link callback route (/magic)
export const loader = async ({ request }) => {
  return await authenticator.authenticate('email-link', request, {
    successRedirect: '/dashboard',
    failureRedirect: '/login',
  });
};

// Example Remix action for a login form
export const action = async ({ request }) => {
  return await authenticator.authenticate('email-link', request, {
    successRedirect: '/login?checkEmail=true', // Redirect to a page asking user to check email
    failureRedirect: '/login',
  });
};

console.log('Authenticator and strategy initialized.');

view raw JSON →