Magic Links

Magic Links

Make sure you're done with setup before configuring authentication methods

This authentication method has two steps:

  1. The user provides an email, to which a link is sent
  2. The user clicks on the link to reopen your web app and is signed-in

Your database keeps track of the issued link "token" that was sent which allows for secure verification and expiration.

For React Native or to improve the user experience use the slightly more advanced OTP method.

Email providers

Convex Auth implements configuration via Auth.js (opens in a new tab) "provider" configs. These JS objects define how the library sends emails.

Open the Auth.js Magic Links providers doc (opens in a new tab) to see a list of available email providers.

Choose an email provider (for example Resend) and follow the guide below.

Ignore the "database provider" configuration in Auth.js docs. Your Convex backend is your database!

Setup

(optional) Verify your email domain

Before you add and verify your email domain to the email provider service of your choice, you might only be able to send emails to the address you signed up with, or your emails might be marked as spam. See the dedicated Auth.js docs page for each provider, for example Resend (opens in a new tab).

Environment variables

Configure the relevant environment variables for a given email provider, as per Auth.js docs, on your Convex backend.

See setting environment variables Convex docs (opens in a new tab).

For example for Resend, after you sign up and obtain your API key, you can run (with your own value):

npx convex env set AUTH_RESEND_KEY yourresendkey

Also check that the SITE_URL variable has the correct port number configured.

Provider configuration

Add the provider config to the providers array in convex/auth.ts.

Import Auth.js providers from @auth/core/providers. For example for Resend:

convex/auth.ts
import Resend from "@auth/core/providers/resend";
import { convexAuth } from "@convex-dev/auth/server";
 
export const { auth, signIn, signOut, store, isAuthenticated } = convexAuth({
  providers: [Resend],
});

Add sign-in form

Now you can trigger the email sending from a form submission via the Convex Auth signIn function.

The first argument to the function is the provider ID, which unless customized is a lowercase version of the provider name. For example for Resend:

src/SignIn.tsx
import { useAuthActions } from "@convex-dev/auth/react";
 
export function SignIn() {
  const { signIn } = useAuthActions();
  return (
    <form
      onSubmit={(event) => {
        event.preventDefault();
        const formData = new FormData(event.currentTarget);
        void signIn("resend", formData);
      }}
    >
      <input name="email" placeholder="Email" type="text" />
      <button type="submit">Send sign-in link</button>
    </form>
  );
}

Check out the example repo (opens in a new tab) for a more polished UI.

You can find more examples of email customization in Auth.js docs (opens in a new tab).

You can control which page the link takes the user to by passing a redirectTo param to the signIn function. See redirect callback.

When you're done configuring your chosen authentication methods, learn how to use authentication in your frontend and backend in Authorization.

Security: Preventing Session Fixation or Phishing

When a user clicks on the link in the email, they will be immediately signed in. This can enable a form of a session fixation attack:

  1. Malicious actor M targets a victim V
  2. M signs up to your app, and creates an account that looks like V's account (for example, uses V's name during sign-up)
  3. M generates a magic link
  4. M makes V click on the link (via a website, email, instant-messaging) or redirects to it from a different webpage
  5. V is now instantly signed in to an account that M has access to. Any data that V provides to your app, will be accessible by M.

To make this kind of an attack less likely to succeed, you can add an interstial page that shows the user which email they are signing-in with, and require a user interaction (button click) to proceed.

You can do this similarly to setting up the OTP method:

  1. Create a custom email provider via Email
  2. Change the link so that it includes the token and the email (but don't use the code URL param)
  3. At the specific path (or based on the URL params), render the interstitial page with a button
  4. When the user clicks on the button, call the signIn function with code and email params.
    • Providers created via Email automatically check that the email matches the email used for sending the token, so the malicious actor cannot spoof it.