NextAuth SMTP email setup lets a Next.js application send passwordless sign-in links through an SMTP relay while keeping SMTP credentials and API keys on the server. This guide uses NextAuth’s email provider, Nodemailer’s SMTP transport, a Prisma database adapter, and Volanea SMTP connection values.

What this integration sends

NextAuth’s email provider sends a transactional verification email when a visitor requests passwordless sign-in. The recipient receives a short-lived magic link; following that link verifies control of the mailbox and completes authentication.

This is an important distinction: NextAuth is not a bulk-email system and its email provider is not designed for newsletters, promotions, or arbitrary marketing sends. It is a good fit for security-sensitive application mail such as sign-in links, account access links, and closely related identity flows.

The actual message delivery path is straightforward:

  1. A user submits an email address from your application’s sign-in screen.
  2. NextAuth creates a verification token through its database adapter.
  3. NextAuth passes the email message to Nodemailer.
  4. Nodemailer authenticates to the Volanea SMTP relay using server-only environment variables.
  5. The relay accepts the message and attempts delivery to the recipient’s mailbox provider.

The sample below deliberately uses SMTP rather than a provider-specific Node SDK. That makes the integration portable and keeps the application aligned with NextAuth’s documented email-provider interface.

Before you begin

This guide targets a Next.js Pages Router project using NextAuth v4 and Prisma. Pinning the major version in the install command matters because NextAuth configuration differs between major releases and between Pages Router and App Router applications.

You need the following before testing:

  • A Next.js application with Node.js installed.
  • SMTP connection information issued for your Volanea account: host, port, username, and the TLS mode expected by that endpoint.
  • An API key or SMTP secret that Volanea instructs you to use as the SMTP password. This guide stores that secret in VOLANEA_API_KEY.
  • A sender address that your Volanea sending configuration is authorized to use.
  • A database for NextAuth’s users, sessions, and verification tokens. The walkthrough uses SQLite locally through Prisma; use a production database supported by Prisma when deploying.
  • A stable application URL and a high-entropy NextAuth secret.

Do not guess the SMTP hostname, port, username format, or whether the endpoint expects implicit TLS. Copy those connection values from the SMTP credentials supplied for your account. A hostname or authentication scheme that works for another email provider is not automatically valid for Volanea.

Install the required packages

Install the exact runtime packages for this NextAuth v4 Pages Router example:

npm install next-auth@4 @next-auth/prisma-adapter@1 @prisma/client nodemailer
npm install -D prisma

Initialize Prisma with SQLite for local development:

npx prisma init --datasource-provider sqlite

The first command adds four runtime dependencies. next-auth handles authentication routes and token flow, @next-auth/prisma-adapter persists the records required by the email provider, @prisma/client queries the database, and nodemailer provides the SMTP transport used by NextAuth.

The second command creates Prisma configuration and an environment file if they do not already exist. SQLite is useful because it gives a new project a database without requiring a separate service. Do not treat a local SQLite file as the default production architecture for a multi-instance deployment: choose a shared production database so every application instance can read the same verification tokens and sessions.

Configure your environment variables

Create or update .env in the project root. Keep this file out of version control. The values below are intentionally placeholders except for the SQLite URL; replace the SMTP fields with the exact values from your Volanea SMTP credentials.

# Prisma local-development database
DATABASE_URL="file:./dev.db"

# The public URL where the Next.js app is running
NEXTAUTH_URL="http://localhost:3000"

# Generate a long, random value. Do not reuse it across unrelated apps.
NEXTAUTH_SECRET="replace-with-a-long-random-secret"

# SMTP endpoint values supplied by Volanea
VOLANEA_SMTP_HOST="replace-with-your-smtp-host"
VOLANEA_SMTP_PORT="587"
VOLANEA_SMTP_SECURE="false"
VOLANEA_SMTP_USERNAME="replace-with-your-smtp-username"

# Store the SMTP password/API key only on the server.
# Use this as the SMTP password only when your issued SMTP credentials specify that mapping.
VOLANEA_API_KEY="replace-with-your-api-key-or-smtp-secret"

# An authorized sender address or mailbox
EMAIL_FROM="Example App <auth@example.com>"

VOLANEA_SMTP_SECURE controls Nodemailer’s secure setting. Set it to true only when the issued SMTP endpoint uses implicit TLS, commonly on port 465. Set it to false for STARTTLS-style connections, commonly on port 587. The port number alone is not a guarantee; follow the connection settings supplied for your SMTP endpoint.

The API key is named VOLANEA_API_KEY here because it is the secret requested by this integration. SMTP always has an authentication model, however, and the credential mapping is provider-specific. This example passes VOLANEA_SMTP_USERNAME as the SMTP username and VOLANEA_API_KEY as the SMTP password. If your Volanea SMTP credentials document a different username/password mapping, preserve the code structure but put the documented values in the appropriate environment variables.

Never prefix a browser-visible variable with NEXT_PUBLIC_ when it contains an API key, SMTP password, or service credential. Next.js exposes NEXT_PUBLIC_ variables to client-side JavaScript at build time. The variables in this guide are read only in the server-side auth route.

For a deployed app, add the same values to the hosting platform’s encrypted environment-variable settings. Also change NEXTAUTH_URL to the canonical HTTPS URL for that deployment. A mismatch between this value and the real public URL can produce magic links with the wrong host, protocol, or callback behavior.

Add the Prisma schema required by NextAuth

Replace the contents of prisma/schema.prisma with the following schema, or merge these models into an existing Prisma schema. This is the database structure expected by the Prisma adapter used in this guide.

generator client {
  provider = "prisma-client-js"
}

datasource db {
  provider = "sqlite"
  url      = env("DATABASE_URL")
}

model Account {
  id                String  @id @default(cuid())
  userId            String
  type              String
  provider          String
  providerAccountId String
  refresh_token     String?
  access_token      String?
  expires_at        Int?
  token_type        String?
  scope             String?
  id_token          String?
  session_state     String?

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)

  @@unique([provider, providerAccountId])
}

model Session {
  id           String   @id @default(cuid())
  sessionToken String   @unique
  userId       String
  expires      DateTime

  user User @relation(fields: [userId], references: [id], onDelete: Cascade)
}

model User {
  id            String    @id @default(cuid())
  name          String?
  email         String?   @unique
  emailVerified DateTime?
  image         String?
  accounts      Account[]
  sessions      Session[]
}

model VerificationToken {
  identifier String
  token      String   @unique
  expires    DateTime

  @@unique([identifier, token])
}

Create the database and generate Prisma Client:

npx prisma migrate dev --name init

NextAuth’s email provider needs an adapter because it must save and later validate a verification token. This persistence requirement is not optional for a production passwordless flow. In particular, do not replace the adapter with process memory: server restarts, serverless execution, and multiple instances would make sign-in links unreliable or invalid.

For an existing application, confirm that your Prisma provider is appropriate for the database you already operate. The model fields can stay conceptually the same while the datasource provider and connection URL change. Run migrations through the deployment process used by your team rather than creating schema changes during application startup.

Add the NextAuth SMTP configuration

Create lib/prisma.ts to reuse Prisma Client during development. The global guard prevents repeated client construction during hot reloads.

import { PrismaClient } from "@prisma/client";

const globalForPrisma = global as unknown as {
  prisma: PrismaClient | undefined;
};

export const prisma = globalForPrisma.prisma ?? new PrismaClient();

if (process.env.NODE_ENV !== "production") {
  globalForPrisma.prisma = prisma;
}

Then create pages/api/auth/[...nextauth].ts. This is the complete server-side NextAuth route. It validates required configuration when the route loads, configures the Prisma adapter, and gives the email provider the SMTP settings Nodemailer needs.

import { PrismaAdapter } from "@next-auth/prisma-adapter";
import type { NextAuthOptions } from "next-auth";
import NextAuth from "next-auth";
import EmailProvider from "next-auth/providers/email";
import { prisma } from "../../../lib/prisma";

function requiredEnv(name: string): string {
  const value = process.env[name];

  if (!value) {
    throw new Error(`Missing required environment variable: ${name}`);
  }

  return value;
}

const smtpPort = Number(requiredEnv("VOLANEA_SMTP_PORT"));

if (!Number.isInteger(smtpPort) || smtpPort < 1 || smtpPort > 65535) {
  throw new Error("VOLANEA_SMTP_PORT must be a valid TCP port number");
}

export const authOptions: NextAuthOptions = {
  adapter: PrismaAdapter(prisma),
  secret: requiredEnv("NEXTAUTH_SECRET"),
  providers: [
    EmailProvider({
      id: "email",
      name: "Email",
      from: requiredEnv("EMAIL_FROM"),
      maxAge: 24 * 60 * 60,
      server: {
        host: requiredEnv("VOLANEA_SMTP_HOST"),
        port: smtpPort,
        secure: process.env.VOLANEA_SMTP_SECURE === "true",
        auth: {
          user: requiredEnv("VOLANEA_SMTP_USERNAME"),
          pass: requiredEnv("VOLANEA_API_KEY"),
        },
      },
    }),
  ],
};

export default NextAuth(authOptions);

This route does not import the SMTP credentials into any client component. When a sign-in request is made, NextAuth creates a verification token through Prisma and uses Nodemailer internally to submit the generated message through the configured SMTP server.

The maxAge value is in seconds. Here it permits the verification token for 24 hours. Set the duration according to the risk profile of the application. Shorter windows reduce the usefulness of a forwarded or exposed link, while longer windows are more forgiving for users who open email late. Whichever value you choose, make it clear in the email copy and avoid treating an email sign-in link as a permanent credential.

Add a sign-in page that triggers one email

The server route is the sending configuration, but users still need a way to request a magic link. Create pages/index.tsx with this minimal form:

import { FormEvent, useState } from "react";
import { signIn } from "next-auth/react";

export default function HomePage() {
  const [email, setEmail] = useState("");
  const [status, setStatus] = useState("");
  const [isSubmitting, setIsSubmitting] = useState(false);

  async function requestMagicLink(event: FormEvent<HTMLFormElement>) {
    event.preventDefault();
    setIsSubmitting(true);
    setStatus("");

    try {
      const result = await signIn("email", {
        email,
        callbackUrl: "/",
        redirect: false,
      });

      if (result?.error) {
        setStatus("Unable to send a sign-in link. Please try again.");
        return;
      }

      setStatus("Check your inbox for a sign-in link.");
    } catch (error) {
      console.error("Magic-link request failed", error);
      setStatus("Unable to send a sign-in link. Please try again.");
    } finally {
      setIsSubmitting(false);
    }
  }

  return (
    <main>
      <h1>Sign in</h1>
      <form onSubmit={requestMagicLink}>
        <label htmlFor="email">Email address</label>
        <input
          id="email"
          name="email"
          type="email"
          autoComplete="email"
          value={email}
          onChange={(event) => setEmail(event.target.value)}
          required
        />
        <button type="submit" disabled={isSubmitting}>
          {isSubmitting ? "Sending…" : "Email me a sign-in link"}
        </button>
      </form>
      {status ? <p role="status">{status}</p> : null}
    </main>
  );
}

This is the requested transactional send in action: submitting a valid address invokes signIn("email", ...), which calls the configured NextAuth email provider and creates one verification-email delivery attempt. The await is essential. It gives the UI a chance to react to the result instead of immediately showing success before the request has completed.

Start the app and submit an inbox you can access:

npm run dev

Open http://localhost:3000, enter the test address, and submit the form. Check the Next.js terminal as well as the destination mailbox. Development mail can be accepted by SMTP but still appear in spam, be deferred, or be rejected downstream if sender-domain authentication and sender authorization are not complete.

How SMTP settings affect delivery

SMTP configuration is more than a connection detail. It is the boundary between your application and the sending infrastructure, and small errors can produce confusing results.

The SMTP host selects the relay. The port and secure combination determines whether the connection begins inside TLS or starts unencrypted and upgrades with STARTTLS. The username and password prove that the application is permitted to submit mail. The from setting supplies the visible sender identity and needs to be authorized by the sending service.

For dependable production delivery, validate these operational details:

  • Use a sender domain you control and configure the domain authentication records required by your sending provider.
  • Ensure the exact mailbox or domain in EMAIL_FROM is authorized for the Volanea account and sending configuration.
  • Keep credentials in the deployment environment, not in committed .env files, source code, browser code, logs, screenshots, or support tickets.
  • Separate development and production credentials so a local test cannot accidentally send using production access.
  • Monitor authentication errors, delivery events, and bounce or complaint signals where your sending infrastructure exposes them.

A successful SMTP acceptance response means the relay accepted the message for processing; it does not necessarily mean that the destination mailbox displayed it in the inbox. Recipient mailbox policies, domain authentication, reputation, content, and recipient state all affect final delivery.

Customize the verification message carefully

NextAuth’s email provider includes a default verification request message. For an application with a branded sign-in experience, customize the provider’s email rendering only after the basic flow works end to end.

A safe custom message should make four things obvious: the application name, the requested action, the destination address or account context where appropriate, and the fact that the recipient can ignore the email if they did not request it. Do not place secrets, API keys, full session data, or personally sensitive data in the body or URL.

Keep the sign-in link intact. The URL is generated by NextAuth and carries the verification information necessary for the flow. Rebuilding it manually is a common cause of expired, malformed, or unverifiable tokens.

Email clients vary substantially in HTML and CSS support, so test the final template in common desktop and mobile clients. A plain-text alternative remains valuable for accessibility, security-conscious users, and clients that do not render HTML. If your messaging needs include transactional layouts beyond authentication, maintain reusable templates with clear variables and preview/test them before releasing changes.

Common errors in a NextAuth SMTP email setup

Authentication fails with SMTP 535, 534, or “authentication failed”

These errors usually mean the SMTP relay rejected the username, password, or credential type. Confirm that VOLANEA_SMTP_USERNAME exactly matches the username issued for the SMTP connection and that VOLANEA_API_KEY is the value intended to be used as the SMTP password.

Do not substitute a REST API bearer token for an SMTP password unless the issued SMTP instructions explicitly say that the same key is valid for SMTP authentication. Also check for copied whitespace, surrounding quotes accidentally included in a deployment secret, revoked keys, and credentials from the wrong environment.

The connection times out, is refused, or reports a TLS error

First verify the host, port, and VOLANEA_SMTP_SECURE setting as one unit. secure: true is for implicit TLS; STARTTLS connections normally begin with secure: false. A mismatch can produce errors such as a socket hang-up, an SSL wrong-version error, or a TLS handshake failure.

Network policy can be the other cause. Some hosting environments restrict outbound SMTP ports, and local corporate networks may do the same. Test from the deployed runtime, not only a laptop, and use the SMTP submission endpoint and port documented for your account.

NextAuth reports that an adapter is required or verification tokens fail

The email provider needs the Prisma adapter to create and consume verification tokens. Confirm that adapter: PrismaAdapter(prisma) remains in authOptions, that DATABASE_URL is available to the server process, and that npx prisma migrate dev --name init completed for local development.

In production, run the appropriate Prisma migration process against the production database. A route can start successfully while failing later if the database is reachable but the required tables do not exist.

The form reports success before a request has completed

This is commonly an async/await mistake. In the sample, await signIn(...) waits for the request result, and finally resets the submit state whether it succeeds or fails. If you remove await, your UI may clear the form or announce success while the network operation is still pending.

On the server, do not attempt to invoke asynchronous mail-sending code during module initialization. Let NextAuth handle the request lifecycle. Module-load sending can run unexpectedly during development reloads, builds, or server starts.

A manual request returns 400 or behaves as though the body is missing

Do not assume that a NextAuth callback endpoint accepts arbitrary JSON. The client-side signIn helper handles the provider flow and request shape for this setup. If you build a custom endpoint around the sign-in flow, parse the content type you actually send and validate input deliberately.

For an API route that expects JSON, clients should send Content-Type: application/json and serialize the body with JSON.stringify. For an HTML form submission, the browser usually uses form encoding instead. Mixing those two formats without matching server-side parsing is a frequent source of invalid-email and missing-field errors.

The message is accepted but never appears in the inbox

Check spam and junk folders first, then verify the sender address is authorized and the sender domain’s authentication is complete. Use an inbox at a different provider for comparison because one mailbox provider’s filtering outcome is not universal.

Also distinguish a NextAuth request failure from a delivery problem. An error in the browser or Next.js log indicates the application did not complete the provider flow. An SMTP acceptance followed by no inbox placement is a delivery and mailbox-filtering investigation.

The magic link opens the wrong domain or fails after deployment

Set NEXTAUTH_URL to the exact public HTTPS origin of the deployed application. Do not leave it as http://localhost:3000 in production. When using preview deployments, decide whether each preview has its own auth URL and callback configuration or whether sign-in should be restricted to a canonical environment.

Changing NEXTAUTH_SECRET invalidates cryptographic state associated with existing sessions and can disrupt active authentication flows. Rotate secrets carefully, store them in a secret manager, and keep environment values consistent across all instances of the same deployment.

Security and production checklist

Passwordless sign-in shifts trust to the recipient mailbox, so protect both the application flow and the email channel. Rate-limit sign-in requests by IP address and by normalized email address to reduce mailbox abuse and infrastructure costs. Avoid revealing whether a submitted address is registered if account enumeration is a concern for your product.

Use HTTPS in production, set a meaningful token lifetime, and provide a clear way for users to request a new link after expiry. Log operational failures with enough context to investigate—such as a request ID, timestamp, and sanitized error class—but never log the magic-link URL, raw verification token, API key, or SMTP password.

Before launch, verify the following:

  1. The sender address is authorized and uses the identity intended for production.
  2. SMTP credentials are stored only in server-side secret settings.
  3. The database is shared and migrated in the production environment.
  4. NEXTAUTH_URL points to the correct public HTTPS origin.
  5. A test recipient can receive, open, and use a link before it expires.
  6. Error pages and support processes do not expose auth tokens or service credentials.
  7. Rate limiting and abuse monitoring cover the endpoint that requests sign-in links.

Next steps: webhooks, templates, and API documentation

Once the basic magic-link path is working, connect delivery operations to your application’s monitoring. If your sending configuration provides webhooks, send their events to a verified server endpoint and use them to record delivery, bounce, complaint, or deferral outcomes. Verify webhook signatures before acting on events, make handlers idempotent, and return successful responses quickly before doing slower background work.

For message presentation, move from a default authentication message to a reviewed template that includes your product identity, support contact, expiration expectations, and an accessible plain-text version. Templates should make transactional intent unmistakable and should not turn a security email into a promotional message.

For related setup material and API-level sending patterns, consult the email API reference and setup guides. Keep authentication mail and general application email operationally separate where that helps you apply different sender identities, monitoring rules, and change controls.

FAQ

Does NextAuth send email directly to recipient inboxes?

No. NextAuth hands the message to Nodemailer, and Nodemailer submits it to the configured SMTP relay. The relay then handles delivery to recipient mailbox providers.

Is VOLANEA_API_KEY safe to use in a Next.js app?

It is safe only when it remains server-side. In this guide it is read in pages/api/auth/[...nextauth].ts; never expose it through a NEXT_PUBLIC_ variable, client component, repository, or browser request.

Why does the email provider need Prisma?

NextAuth needs durable storage for users, sessions, and verification tokens. The Prisma adapter supplies that storage layer, allowing a magic link generated during one request to be validated later.

Should VOLANEA_SMTP_SECURE be true for port 587?

Usually no for STARTTLS-style SMTP submission, but use the TLS mode documented for the specific SMTP endpoint. Set secure to true only for implicit TLS endpoints.

Can this configuration send a newsletter?

No. This setup is for NextAuth’s transactional sign-in email. Use a dedicated campaign workflow for bulk marketing messages, consent management, unsubscribe handling, and campaign-specific reporting.