SecurityRate limiting (Magic Links)

NextAuth does not have a built-in functionality to rate limit magic link emails.

Instead, you can use a service like Upstash to secure your /api/auth/signin/email endpoint (responsible for signing up with magic links).





Setup
  1. Sign up on Upstash

  2. Create a new Redis database

  3. Add the UPSTASH_REDIS_REST_URL and UPSTASH_REDIS_REST_TOKEN to your .env.local file

  4. Install the packages:

terminal
npm install @upstash/redis @upstash/ratelimit
  1. Create a new middleware.js file in the root directory (the same level as the /app folder) and add the following content:
/middleware.js
import { NextResponse } from "next/server";
import { Ratelimit } from "@upstash/ratelimit";
import { Redis } from "@upstash/redis";
 
const redis = new Redis({
  url: process.env.UPSTASH_REDIS_REST_URL,
  token: process.env.UPSTASH_REDIS_REST_TOKEN,
});
 
const ratelimit = new Ratelimit({
  redis: redis,
  limiter: Ratelimit.slidingWindow(5, "60 s"),
});
 
export default async function middleware(request) {
  const ip = request.ip ?? "127.0.0.1";
  const { success } = await ratelimit.limit(ip);
 
  return success
    ? NextResponse.next()
    : NextResponse.redirect(new URL("/blocked", request.url));
}
 
export const config = {
  matcher: ["/api/auth/signin/email"],
};
💡

We are rate limiting the user to 5 requests per minute based on their IP using the sliding window algorithm. You can refer to the Upstash ratelimit SDK documentation for more information on customizing it.

  1. Create a new /app/blocked/page.js file. This is the page the user will be redirected to when they hit the rate limit. Add the following content:
/app/blocked/page.js
"use client";
 
import config from "@/config";
import { signIn } from "next-auth/react";
import React from "react";
import Link from "next/link";
 
const Blocked = () => {
  return (
    <main className="relative bg-neutral text-neutral-content h-screen w-full flex flex-col justify-center gap-8 items-center p-10">
      <h1 className="text-xl md:text-2xl font-medium">Hm, Access Blocked</h1>
      <p>Try again in 1 minute</p>
 
      <div>
        <button
          onClick={() =>
            signIn(undefined, {
              callbackUrl: config.auth.callbackUrl,
            })
          }
          className="link"
        >
          Login
        </button>{" "}
        or{" "}
        <Link className="link" href="/">
          Home
        </Link>
      </div>
    </main>
  );
};
 
export default Blocked;
  1. That’s it! You have successfully rate limited the Magic Link sign-ins. Now, when a user hits the rate limit, they will be redirected to the /blocked page.
Last updated on