Search code examples
next.jssendgridnext-auth

Next auth email provider is not triggering


Next-Auth has been working great for me until i decided to create a custom signIn page.

I have google login working perfectly and i can see lots going on in the debugger around token generation and session creation with my adapter.

However, when I try to sign in using email verification it just clears the email from the input box and then does nothing. The Form is straight out of the next auth docs.

I have triple checked the settings for my sendgrid apikey and they are all set correctly in my .env.local file.

"/pages/api/auth/[...nextauth].js"

import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"
import EmailProvider from "next-auth/providers/email"
import { FirestoreAdapter } from "@next-auth/firebase-adapter"

const authOptions = {
  providers: [
    GoogleProvider({
      clientId: process.env.GOOGLE_ID,
      clientSecret: process.env.GOOGLE_SECRET,
    }),
    EmailProvider({
      server: {
        host: process.env.EMAIL_SERVER_HOST,
        port: process.env.EMAIL_SERVER_PORT,
        auth: {
          user: process.env.EMAIL_SERVER_USER,
          pass: process.env.EMAIL_SERVER_PASSWORD,
        }
      },
      from: process.env.EMAIL_FROM
    }),
  ],
  session: {
    jwt: true,
  },
  adapter: FirestoreAdapter({
    apiKey: process.env.FIREBASE_API_KEY,
    appId: process.env.FIREBASE_APP_ID,
    authDomain: process.env.FIREBASE_AUTH_DOMAIN,
    databaseURL: process.env.FIREBASE_DATABASE_URL,
    projectId: process.env.FIREBASE_PROJECT_ID,
    storageBucket: process.env.FIREBASE_STORAGE_BUCKET,
    messagingSenderId: process.env.FIREBASE_MESSAGING_SENDER_ID
  }),
  theme: ({
    colorScheme: "dark",
  }),
  pages: {
    signIn: "/auth/signin",
    newUser: "/auth/signup",
  },
  debug: true,
  callbacks: {
    async signIn({ user, account, profile, email, credentials }) {
      return true
    },
    async redirect({ url, baseUrl }) {
      return baseUrl
    },
    async session({ session, user, token }) {
      return session
    },
    async jwt({ token, user, account, profile, isNewUser }) {
      return token
    }
  }
}
export default NextAuth(authOptions)

Here is my custom page...

"/pages/auth/signin.js"

import React from 'react'
import {getProviders, signIn, getCsrfToken} from 'next-auth/react'
import styles from '../../styles/signin.module.css'
import Logo from '../../components/Logo'

export default function SignIn ({ csrfToken, providers }) {

  return (
    <div>
      <div className={styles.content}>
        <div className={styles.cardWrapper}>
          <Logo className={styles.logo}/>
          <div className={styles.cardContent}>
            <div className={styles.emailForm}>
              <form method="post" action="/api/auth/signin/email">
                <input name="csrfToken" type="hidden" defaultValue={csrfToken} />
                <input type="text" id="email" name="email" placeholder="Email" />
                <button type="submit">Sign in with Email</button>
              </form>
            </div>
            <hr />
            {Object.values(providers).map((provider) => {
              if (provider.name === "Email") {
                return
              }
              return (
                <div key={provider.name}>
                  <button onClick={() => signIn(provider.id)}>
                    Sign in with {provider.name}
                  </button>
                </div>
              )
            })}
          </div>
        </div>
      </div>
    </div>
  )
}

export async function getServerSideProps(context) {
  const csrfToken = await getCsrfToken(context)
  const providers = await getProviders(context)
  return {
    props: { csrfToken },
    props: { providers },
  }
}

EDIT: Installed Dependencies

"next": "^12.2.5",
"next-auth": "^4.10.3",
"nodemailer": "^6.7.8",
"react": "18.2.0",
"react-dom": "18.2.0",
"swiper": "^8.3.2",
"swr": "^1.3.0"
"@next-auth/firebase-adapter": "^1.0.1",
"@types/node": "^18.7.13",
"core-js": "^3.25.0",
"firebase": "^9.9.3",
"fs": "^0.0.1-security",

EDIT: ENV SETTINGS

NEXTAUTH_URL=http://localhost:3000
NEXTAUTH_SECRET=REDACTED SECRET

EMAIL_SERVER_HOST=smtp.sendgrid.net
EMAIL_SERVER_PORT=587
EMAIL_SERVER_USER=apikey
EMAIL_SERVER_PASSWORD=REDACTED (SET AS THE API KEY PROVIDED BY SENDGRID)
EMAIL_FROM=REDACTED (SET AS VERIFIED SINGLE USER EMAIL ADDRESS IN SENDGRID)

Also set are the following...

FIREBASE_API_KEY=REDACTED
FIREBASE_AUTH_DOMAIN=REDACTED
FIREBASE_PROJECT_ID=REDACTED
FIREBASE_STORAGE_BUCKET=REDACTED
FIREBASE_MESSAGING_SENDER_ID=REDACTED
FIREBASE_APP_ID=REDACTED
FIREBASE_DATABASE_URL=REDACTED

EDIT: "/pages/_app.js" - Added main app just in case the problem is being caused by the way I have wrapped my component.

import React from 'react'
import App from 'next/app'
import { SessionProvider } from "next-auth/react"
import Layout from "../components/Layout.js"
import AccountSettingsLayout from '../components/AccountSettingsLayout'
import { SWRConfig } from 'swr'


import '../styles/globals.css'

class MyApp extends App {
  render() {
    const { Component, router, pageProps: { session, ...pageProps }} = 
this.props

  if (router.pathname.startsWith('/auth/')) {
    return (
      <React.StrictMode>
        <SessionProvider session={session}>
          <SWRConfig>
            <AccountSettingsLayout>
              <Component {...pageProps} />
            </AccountSettingsLayout>
          </SWRConfig> 
        </SessionProvider>
      </React.StrictMode>
    )
  }

  return (
    <React.StrictMode>
      <SessionProvider session={session}>
        <SWRConfig>
          <Layout>
            <Component {...pageProps} />
          </Layout>
        </SWRConfig> 
      </SessionProvider>
    </React.StrictMode>
  )
}}

export default MyApp

UPDATE: when the email address is entered and the submit is clicked i get the following change in the URL.

starting URL = http://localhost:3000/auth/signin

after submit= http://localhost:3000/auth/signin?callbackUrl=http%3A%2F%2Flocalhost%3A3000

UPDATED 30.08.2022 - I can see in terminal the following... wait - compiling /_error (client and server)... However the event compiles straight after without any error being displayed.

I read in the next.js docs that the payload for email must 2 entries, the CSRF token and the email address. I can see in the browser tools that the CSRF token is generated. I can also see in the network tab that the payload shows the email address but the CSRF Token is blank on submission.

Thanks in advance for any help on this one. Go easy on me as I am a new coder!


Solution

  • Ok so the problem was with my getServerSideProps...

    BEFORE:

    export async function getServerSideProps(context) {
      const csrfToken = await getCsrfToken(context)
      const providers = await getProviders(context)
      return {
        props: { csrfToken },
        props: { providers },
      }
    }
    

    AFTER:

    export async function getServerSideProps(context) {
      const csrfToken = await getCsrfToken(context)
      const providers = await getProviders(context)
      return {
        props: { csrfToken, providers },
      }
    }
    

    I noticed that the csrfToken was not being passed into the email submit payload.

    For others in future...using chrome devtools if you inspect your page prior to clicking submit then you can click on network and check the payload and header.