---
title: Magic link
description: Magic link plugin
---

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

<Steps>
  <Step>
    ### Add the server Plugin

    Add the magic link plugin to your server:

    ```ts title="server.ts"
    import { betterAuth } from "better-auth";
    import { magicLink } from "better-auth/plugins";  // [!code highlight]

    export const auth = betterAuth({
        plugins: [
            magicLink({ // [!code highlight]
                sendMagicLink: async ({ email, token, url, metadata }, ctx) => { // [!code highlight]
                    // send email to user // [!code highlight]
                } // [!code highlight]
            }) // [!code highlight]
        ]
    })
    ```
  </Step>

  <Step>
    ### Add the client Plugin

    Add the magic link plugin to your client:

    ```ts title="auth-client.ts"
    import { createAuthClient } from "better-auth/client";
    import { magicLinkClient } from "better-auth/client/plugins"; // [!code highlight]

    export const authClient = createAuthClient({
        plugins: [
            magicLinkClient() // [!code highlight]
        ]
    });
    ```
  </Step>
</Steps>

## Usage

### Sign In with Magic Link

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.

<APIMethod path="/sign-in/magic-link" method="POST" requireSession>
  ```ts
  type signInMagicLink = {
      /**
       * Email address to send the magic link. 
       */
      email: string = "user@email.com"
      /**
       * User display name. Only used if the user is registering for the first time. 
       */
      name?: string = "my-name"
      /**
       * URL to redirect after magic link verification. 
       */
      callbackURL?: string = "/dashboard"
      /**
       * URL to redirect after new user signup
       */
      newUserCallbackURL?: string = "/welcome"
      /**
       * 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.
       */
      errorCallbackURL?: string = "/error"
      /**
       * Additional metadata forwarded to the sendMagicLink callback.
       */
      metadata?: Record<string, any> = { inviteId: "123" }
  }
  ```
</APIMethod>

<Callout>
  If the user has not signed up, unless `disableSignUp` is set to `true`, the user will be signed up automatically.
</Callout>

### Verify Magic Link

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.

<Callout type="warn">
  If no `callbackURL` is provided, the user will be redirected to the root URL.
</Callout>

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

<APIMethod path="/magic-link/verify" method="GET" requireSession>
  ```ts
  type magicLinkVerify = {
      /**
       * Verification token. 
       */
      token: string = "123456"
      /**
       * URL to redirect after magic link verification, if not provided will return the session. 
       */
      callbackURL?: string = "/dashboard"
  }
  ```
</APIMethod>

## 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.
* `metadata`: Additional request metadata passed from `signIn.magicLink`.

and a `ctx` context 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).

**allowedAttempts**: Specifies the number of allowed attempts for verifying the magic link. The default value is `1`. When the limit is exceeded, the token is deleted and the user is redirected with `?error=ATTEMPTS_EXCEEDED`. Set to `Infinity` to allow unlimited attempts.

**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.

<Callout type="warn">
  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.
</Callout>

**storeToken**: The `storeToken` function controls how the magic link token is transformed before it is stored by Better Auth's verification layer. 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.

The storage backend itself is controlled by the global [`verification`](/docs/reference/options#verification) config. If you configure `secondaryStorage`, magic link verification records can be stored there instead of the database.
