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
- Sign up on Upstash
- Create a new Redis database
- Add the
UPSTASH_REDIS_REST_URL
andUPSTASH_REDIS_REST_TOKEN
to your.env.local
file - Install the packages:
terminal
1npm install @upstash/redis @upstash/ratelimit
- Create a new
middleware.js
file in the root directory (the same level as the/app
folder) and add the following content:/middleware.js
1import { NextResponse } from "next/server"; 2import { Ratelimit } from "@upstash/ratelimit"; 3import { Redis } from "@upstash/redis"; 4 5const redis = new Redis({ 6 url: process.env.UPSTASH_REDIS_REST_URL, 7 token: process.env.UPSTASH_REDIS_REST_TOKEN, 8}); 9 10const ratelimit = new Ratelimit({ 11 redis: redis, 12 limiter: Ratelimit.slidingWindow(5, "60 s"), 13}); 14 15export default async function middleware(request) { 16 const ip = request.ip ?? "127.0.0.1"; 17 const { success } = await ratelimit.limit(ip); 18 19 return success 20 ? NextResponse.next() 21 : NextResponse.redirect(new URL("/blocked", request.url)); 22} 23 24export const config = { 25 matcher: ["/api/auth/signin/email"], 26}; 27
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. - 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
1"use client"; 2 3import config from "@/config"; 4import { signIn } from "next-auth/react"; 5import React from "react"; 6import Link from "next/link"; 7 8const Blocked = () => { 9 return ( 10 <main className="relative bg-neutral text-neutral-content h-screen w-full flex flex-col justify-center gap-8 items-center p-10"> 11 <h1 className="text-xl md:text-2xl font-medium"> 12 Hm, Access Blocked 13 </h1> 14 <p>Try again in 1 minute</p> 15 16 <div> 17 <button 18 onClick={() => 19 signIn(undefined, { 20 callbackUrl: config.auth.callbackUrl, 21 }) 22 } 23 className="link" 24 > 25 Login 26 </button>{" "} 27 or{" "} 28 <Link className="link" href="/"> 29 Home 30 </Link> 31 </div> 32 </main> 33 ); 34}; 35 36export default Blocked;
- 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.