Magic link

Magic link or email link is a way to authenticate users without a password. When a user enters their email, a link is sent to their email. When the user clicks on the link, they are authenticated.

Installation

Add the server Plugin

Add the magic link plugin to your server:

server.ts
import { betterAuth } from "better-auth";
import { magicLink } from "better-auth/plugins";
 
export const auth = betterAuth({
    plugins: [
        magicLink({
            sendMagicLink: async ({ email, token, url }, request) => {
                // send email to user
            }
        })
    ]
})

Add the client Plugin

Add the magic link plugin to your client:

auth-client.ts
import { createAuthClient } from "better-auth/client";
import { magicLinkClient } from "better-auth/client/plugins";
export const authClient = createAuthClient({
    plugins: [
        magicLinkClient()
    ]
});

Usage

To sign in with a magic link, you need to call signIn.magicLink with the user's email address. The sendMagicLink function is called to send the magic link to the user's email.

POST
/sign-in/magic-link
const { data, error } = await authClient.signIn.magicLink({
    email: "[email protected]", // required
    name: "my-name",
    callbackURL: "/dashboard",
    newUserCallbackURL: "/welcome",
    errorCallbackURL: "/error",
});
PropDescriptionType
email
Email address to send the magic link.
string
name?
User display name. Only used if the user is registering for the first time.
string
callbackURL?
URL to redirect after magic link verification.
string
newUserCallbackURL?
URL to redirect after new user signup
string
errorCallbackURL?
URL to redirect if an error happen on verification If only callbackURL is provided but without an errorCallbackURL then they will be redirected to the callbackURL with an error query parameter.
string

If the user has not signed up, unless disableSignUp is set to true, the user will be signed up automatically.

When you send the URL generated by the sendMagicLink function to a user, clicking the link will authenticate them and redirect them to the callbackURL specified in the signIn.magicLink function. If an error occurs, the user will be redirected to the callbackURL with an error query parameter.

If no callbackURL is provided, the user will be redirected to the root URL.

If you want to handle the verification manually, (e.g, if you send the user a different URL), you can use the verify function.

GET
/magic-link/verify
const { data, error } = await authClient.magicLink.verify({
    token: "123456", // required
    callbackURL: "/dashboard",
});
PropDescriptionType
token
Verification token.
string
callbackURL?
URL to redirect after magic link verification, if not provided will return the session.
string

Configuration Options

sendMagicLink: The sendMagicLink function is called when a user requests a magic link. It takes an object with the following properties:

  • email: The email address of the user.
  • url: The URL to be sent to the user. This URL contains the token.
  • token: The token if you want to send the token with custom URL.

and a request object as the second parameter.

expiresIn: specifies the time in seconds after which the magic link will expire. The default value is 300 seconds (5 minutes).

disableSignUp: If set to true, the user will not be able to sign up using the magic link. The default value is false.

generateToken: The generateToken function is called to generate a token which is used to uniquely identify the user. The default value is a random string. There is one parameter:

  • email: The email address of the user.

When using generateToken, ensure that the returned string is hard to guess because it is used to verify who someone actually is in a confidential way. By default, we return a long and cryptographically secure string.

storeToken: The storeToken function is called to store the magic link token in the database. The default value is "plain".

The storeToken function can be one of the following:

  • "plain": The token is stored in plain text.
  • "hashed": The token is hashed using the default hasher.
  • { type: "custom-hasher", hash: (token: string) => Promise<string> }: The token is hashed using a custom hasher.

On this page