简体   繁体   中英

How do I add data to the client API in next-auth?

I'm currently using next-auth for authorisation using the credentials provider, I have sessions working and the user can login etc. However, on the session I need to pass in some data using the client API, the users, firstname, lastname, username and email.

By default the client API passes, name, email and image, however, how do I change this to add the above data, here is what I have so far.

index.js

import { useState, useEffect  } from 'react';
import { getSession } from 'next-auth/client'
import { useRouter } from 'next/router';
import Link from 'next/link';
import Head from 'next/head';
import Sidebar from '../components/Sidebar';

export default function Dashboard({ user}) {
  return (
    <div>
      <Head>
        <title>Dashboard</title>
      </Head>

      <Sidebar />

      <section className="content dashboard-content">
        <h1>Dashboard</h1>

        <h3>Welcome to Ellis development {user.firstname }</h3>
      </section>
    </div>
  )
}

export async function getServerSideProps(ctx) {
  const session = await getSession(ctx);
  
  if (!session) {
    return {
      redirect: {
        destination: '/dashboard/auth/login',
        permanent: false
      },
    }
  }

  console.log(session);

  return {
    props: {
      user: {
        firstname: session.user.firstname,
        lastname: session.user.lastname,
        username: session.user.username,
        email: session.user.email,
      }
    },
  }
}

[...nextauth.js]

import NextAuth from 'next-auth';
import Providers from 'next-auth/providers';

import { verifyPassword } from '../../../lib/auth';
import { connectToDatabase } from '../../../lib/mongodb';

export default NextAuth({
  session: {
    jwt: true,
  },
  providers: [
    Providers.Credentials({
      async authorize(credentials) {
        const client = await connectToDatabase();
        const usersCollection = client.db().collection('users');

        const user = await usersCollection.findOne({
          email: credentials.email,
        });

        if (!user) {
          client.close();
          throw new Error('No user found!');
        }

        const isValid = await verifyPassword(
          credentials.password,
          user.password
        );

        if (!isValid) {
          client.close();
          throw new Error('Could not log you in!');
        }

        client.close();

        return {
          firstname: user.firstname,
          lastname: user.lastname,
          username: user.username,
          email: user.email
        };
      },
    }),
  ],
});

Any help would be great, thanks.

edit

I've added the following to the [...next-auth] page

callbacks: {
  session: async (session) => {
    if (!session) return;

    const client = await connectToDatabase();
    const usersCollection = client.db().collection('users');
    
    const userData = await usersCollection.findOne({
      email: session.user.email,
    });

    return {
      session: {
        user: {
          id: userData._id,
          firstname: userData.firstname,
          lastname: userData.lastname,
          username: userData.username,
          email: userData.email
        }
      }
    };
  },
},

which gives me the following result

{
  session: {
    user: {
      id: '61a107f29ca24c12146d1b22',
      firstname: 'Ben',
      lastname: 'Bagley',
      username: 'benbagley',
      email: 'benbagley@pm.me'
    }
  }
}

So I now have the values I need, however, how do I go rendering the data onto the page I now have the following

import { getSession } from 'next-auth/client'
import Head from 'next/head';
import Sidebar from '../components/Sidebar';

export default function Dashboard({ session }) {
  return (
    <div>
      <Head>
        <title>Dashboard</title>
      </Head>

      <Sidebar />

      <section className="content dashboard-content">
        <h1>Dashboard</h1>

        <h3>Welcome {session.user.firstname} to Ellis development</h3>
      </section>
    </div>
  )
}

export async function getServerSideProps(ctx) {
  const session = await getSession(ctx);
  
  if (!session) {
    return {
      redirect: {
        destination: '/dashboard/auth/login',
        permanent: false
      },
    }
  }

  console.log(session);

  return {
    props: {
      session: {
        user: {
          id: session.user.id,
          firstname: session.user.firstname,
          lastname: session.user.lastname,
          username: session.user.username,
        }
      }
    },
  }
}

However, I am getting TypeError: Cannot read properties of undefined (reading 'id')

You should checkout next-auth callbacks. The user object will contain email, image and name. You can use it to fetch an internal api or so, and append the info to the session object which will be encoded in the jwt.

callbacks: {
        session: async (session, user) => {
            if (!session) return;
            const userServerData = fetch(...); // or some internal logic
            
            session.user.firstName = userServerData.firstName;
            session.user.lastname = userServerData.lastname;
            session.user.username = userServerData.username;
            
            return Promise.resolve(session);
        },
    },

This is how I solved the same problem in my project

Step 1: Add the following to...[nextauth].ts

  pages: {
    // signIn: '/auth/signin',
    // signOut: '/auth/signout',
    // error: '/auth/error', // Error code passed in query string as ?error=
    // verifyRequest: '/auth/verify-request', // (used for check email message)
    newUser: '/auth/newuser' // New users will be directed here on first sign in 
  },

Step 2: Now you can update user data and post to your api

'/auth/newuser.ts'

    React.useEffect(() => {
   

       post updated user data to -> "/api/auth/newuserwelcome"

      }, [])

//
Step 3: create api endpoint to save updated user data to DB
    /api/auth/newuserwelcome.ts
      

The issue here was two fold,

a) Not using a proper callback to add in and overwrite the next-auth api, for example:

callbacks: {
  session: async (session) => {
    if (!session) return;

    const client = await connectToDatabase();
    const usersCollection = client.db().collection('users');
    
    const userData = await usersCollection.findOne({
      email: session.user.email,
    });

    return {
      session: {
        user: {
          id: userData._id,
          firstname: userData.firstname,
          lastname: userData.lastname,
          username: userData.username,
          email: userData.email
        }
      }
    };
  },
},

Now that this is passing in the values, the next issue props up...

b) Not using the spread operator when passing props

export async function getServerSideProps(ctx) {
  const session = await getSession(ctx);
  
  if (!session) {
    return {
      redirect: {
        destination: '/dashboard/auth/login',
        permanent: false
      },
    }
  }

  return {
    props: {
      ...session,
    }
  }
}

Calling ...session gets all of the return object and allows it to be passes as such session.user.firstname , very handy.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM