Discussions

Problème callbacks Next version 13

Image

Bonjour !

 

Je rencontre de gros problemes cote serveur avec la mise en place de callbacks.

 

Voici mon code :

// Librairies
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import { connectToDataBase } from '../../../helpers/mongodb';
import { verifyPassword } from '../../../helpers/auth';

export const authOptions = {
  // session: {
  //   maxAge: 30 * 24 * 60 * 60, // 30 days
  //   generateSessionToken: () => {
  //     return randomUUID?.() ?? randomBytes(32).toString('hex');
  //   },
  // },

  // Ajout des informations de l'user (email, pseudo, password)
  providers: [
    CredentialsProvider({
      async authorize(credentials) {
        const { email, password } = credentials;

        // Connexion à MongoDB
        const clientMongoDB = await connectToDataBase();

        // Etape 1 - Utilisateur existant ?
        const user = await clientMongoDB
          .db()
          .collection('users')
          .findOne({ email: email });

        if (!user) {
          clientMongoDB.close();
          throw new Error(
            'Impossible de vous authentifier, merci de vérifier vos credentials.'
          );
        }

        // Etape 2 - Mot de passe est-il correct vs celui enregistré ?
        const isValid = await verifyPassword(password, user.password);

        if (!isValid) {
          clientMongoDB.close();
          throw new Error(
            'Impossible de vous authentifier, merci de vérifier vos credentials.'
          );
        }

        // Etape 3 - Succès
        clientMongoDB.close();
        return {
          email: user.email,
          name: user.pseudo,
          id: user._id,
          roles: user.roles,
        };
      },
    }),
  ],

  // Ajout de nouvelles informations (cookies) à notre session user
  // callbacks: {
  //   // Récupération du token
  //   jwt: async (token, _user) => {
  //     _user && (token.user = _user);
  //     return token;
  //   },
  //   // Récupération du user
  //   session: async (session, _user) => {
  //     session.user = _user.user;
  //     return session;
  //   },
  // },
};

export default NextAuth(authOptions);

voici le message d'erreur en question :

[next-auth][error][JWT_SESSION_ERROR] 

https://next-auth.js.org/errors#jwt_session_error Cannot read properties of undefined (reading 'user') {

  message: "Cannot read properties of undefined (reading 'user')",

  stack: "TypeError: Cannot read properties of undefined (reading 'user')\n" +  

    '    at Object.session (webpack-internal:///(api)/./src/pages/api/auth/[...nextauth].js:65:34)\n' +

    '    at Object.session (C:\\Users\\Lordtoinou\\Desktop\\Believemy Formation\\Next\\projet1-next\\node_modules\\next-auth\\core\\routes\\session.js:56:42)\n' +

    '    at async AuthHandler (C:\\Users\\Lordtoinou\\Desktop\\Believemy Formation\\Next\\projet1-next\\node_modules\\next-auth\\core\\index.js:158:27)\n' +    

    '    at async NextAuthHandler (C:\\Users\\Lordtoinou\\Desktop\\Believemy Formation\\Next\\projet1-next\\node_modules\\next-auth\\next\\index.js:24:19)\n' + 

    '    at async C:\\Users\\Lordtoinou\\Desktop\\Believemy Formation\\Next\\projet1-next\\node_modules\\next-auth\\next\\index.js:60:32\n' +

    '    at async Object.apiResolver (C:\\Users\\Lordtoinou\\Desktop\\Believemy Formation\\Next\\projet1-next\\node_modules\\next\\dist\\server\\api-utils\\node.js:372:9)\n' +

    '    at async DevServer.runApi (C:\\Users\\Lordtoinou\\Desktop\\Believemy Formation\\Next\\projet1-next\\node_modules\\next\\dist\\server\\next-server.js:514:9)\n' +

    '    at async Object.fn (C:\\Users\\Lordtoinou\\Desktop\\Believemy Formation\\Next\\projet1-next\\node_modules\\next\\dist\\server\\next-server.js:828:35)\n' +

    '    at async Router.execute (C:\\Users\\Lordtoinou\\Desktop\\Believemy Formation\\Next\\projet1-next\\node_modules\\next\\dist\\server\\router.js:243:32)\n' +

    '    at async DevServer.runImpl (C:\\Users\\Lordtoinou\\Desktop\\Believemy Formation\\Next\\projet1-next\\node_modules\\next\\dist\\server\\base-server.js:432:29)\n' +

    '    at async DevServer.run (C:\\Users\\Lordtoinou\\Desktop\\Believemy Formation\\Next\\projet1-next\\node_modules\\next\\dist\\server\\dev\\next-dev-server.js:831:20)\n' +

    '    at async DevServer.handleRequestImpl (C:\\Users\\Lordtoinou\\Desktop\\Believemy Formation\\Next\\projet1-next\\node_modules\\next\\dist\\server\\base-server.js:375:20)\n' +

    '    at async C:\\Users\\Lordtoinou\\Desktop\\Believemy Formation\\Next\\projet1-next\\node_modules\\next\\dist\\server\\base-server.js:157:99',

  name: 'TypeError'

}

 

 

Comment est-il possible de résoudre svp ? Merci par avance !

Antoine Delamare
Blockchain & Cie.
4 réponses
Image
Louis-Nicolas Leuillet
Le 22/03/2023 à 12:59

Bonjour !

 

Le message d'erreur que vous avez partagé semble indiquer que le champ "user" est undefined dans votre callback "session". Il est probable que cela soit dû à une erreur de syntaxe ou de logique dans votre code.

 

Dans votre callback "session", vous récupérez "_user" en paramètre, et vous essayez d'accéder à "_user.user". Cela peut poser problème si "_user" est undefined ou null.

 

Pour éviter cette erreur, vous pouvez ajouter une vérification pour vous assurer que "_user" n'est pas undefined avant d'essayer d'y accéder :

callbacks: {
    // Le code avant
  session: async (session, _user) => {
    if (_user && _user.user) {
      session.user = _user.user;
    }
    return session;
  }
}
Image
Antoine Delamare
Le 22/03/2023 à 14:01

Merci pour le retour rapide !

 

Malheureusement a quelques modules de la fin de la formation, le projet se montre capricieux et je me retrouve démuni.

 

Voici les codes de :

- index.js (page d'accueil) :

// Librairies
import CarteProjet from '../Components/CarteProjet/CarteProjet';
import { connectToDataBase } from '../helpers/mongodb';
import Head from 'next/head';
import Image from 'next/image';
import { signOut } from 'next-auth/react';
import { getServerSession } from 'next-auth/next';
import { useState } from 'react';

// Components
import { SpinnerCircularFixed } from 'spinners-react';
import { authOptions } from './api/auth/[...nextauth]';

// Ecrire le default de cette manière
export default function Index(props) {
  // STATES
  // State 1 - isLoading
  const [isLoading, setIsLoading] = useState(false);

  // State 2 - Error
  const [error, setError] = useState();

  // METHODES
  // Méthode 1 - deleteClickedHandler
  const onDeleteClickedHandler = async () => {
    if (!isLoading) {
      setIsLoading(true);
      setError(null);

      // Envoyer la demande de suppression
      const response = await fetch('/api/user/delete', {
        method: 'DELETE',
        headers: {
          'Content-Type': 'application/json',
        },
      });

      const fetchedData = await response.json();

      if (!response.ok) {
        setIsLoading(false);
        setError(fetchedData.message || 'Une erreur est survenue.');
      } else {
        setIsLoading(false);
        signOut();
      }
    }
  };

  // JSX
  return (
    <main>
      <Head>
        <title>Le portfolio d'un dev blockchain (Antoine)</title>
      </Head>
      <h1>
        Bienvenue {props.user ? props.user.name : 'sur mon portfolio'}
      </h1>
      <div
        style={{
          border: '2px solid #ee6c4d',
          padding: '10px',
          borderRadius: '15px',
          display: 'flex',
          justifyContent: 'space-beetween',
          alignItems: 'center',
          gap: '30px',
        }}>
        <div>
          <h2 style={{ fontWeight: 'lighter' }}>
            Je m'appelle <b>Antoine Delamare</b>
          </h2>
          <p>
            Je suis un développeur fullstack blockchain junior depuis
            fin 2022. Passionné de la blockchain depuis 2019, je
            partage mon expérience dans le développement de solutions
            blockchain pour les entreprises et les organisations.
            Envie de collaborations futures ?
          </p>
          <p>
            <a
              href='mailto:adelamare.blockchain@gmail.com'
              style={{
                display: 'inline-block',
                background: '#ee6c4d',
                color: 'white',
                textDecoration: 'none',
                padding: '10px 15px',
                borderRadius: '5px',
              }}>
              Contactez-moi !
            </a>

            {props.user && (
              <button
                style={{
                  cursor: 'pointer',
                  background: '#ee6c4d',
                  color: 'white',
                  marginLeft: '10px',
                  border: 'none',
                  padding: '10px 15px',
                  borderRadius: '5px',
                }}
                onClick={onDeleteClickedHandler}>
                {isLoading ? (
                  <SpinnerCircularFixed
                    size={15}
                    thickness={100}
                    speed={100}
                    color='#ffffff'
                    secondaryColor='rgba(0, 0, 0, 0.44)'
                  />
                ) : (
                  <svg
                    xmlns='http://www.w3.org/2000/svg'
                    style={{
                      width: '15px',
                      height: '15px',
                    }}
                    fill='none'
                    viewBox='0 0 24 24'
                    strokeWidth={1.5}
                    stroke='currentColor'
                    className='w-6 h-6'>
                    <path
                      strokeLinecap='round'
                      strokeLinejoin='round'
                      d='M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0'
                    />
                  </svg>
                )}
              </button>
            )}
          </p>
        </div>
        <div>
          <div
            style={{
              borderRadius: '50%',
              overflow: 'hidden',
              lineHeight: 0,
            }}>
            <Image
              src='/moi.jpg'
              alt='Antoine Delamare'
              width={150}
              height={150}
              // layout='fixed'
              // layout='responsive'
              // unoptimized
              // quality={100}
              // priority={true}
              // loading='eager'
            />
          </div>
        </div>
      </div>

      <h2 style={{ fontWeight: 'lighter' }}>
        <b>Mes 3 derniers projets</b>

        <div
          style={{
            display: 'grid',
            gridTemplateColumns: 'repeat(3, 1fr)',
            gap: '10px',
          }}>
          {props.projets.map((projet) => (
            <CarteProjet projet={projet} key={projet._id} />
          ))}
        </div>
      </h2>
    </main>
  );
}

// PRE-RENDU : Invisible de l'utilisateur : sur le serveur
export async function getServerSideProps(context) {
  // VARIABLES
  // Variable 1 - projets
  let projets;

  // Variable 2 - session de l'user
  const session = await getServerSession(
    context.req,
    context.res,
    authOptions
  );

  let username = null;

  if (session) {
    username = session.user.name;
    console.log(username);
  }

  try {
    // Connexion a MongoDB
    const client = await connectToDataBase();
    const db = client.db();

    // Récupérer les projets
    projets = await db
      .collection('projets')
      .find()
      .sort({ dateDePublication: 'desc' })
      // .limit(3) ==> Méthode 1 - récuperation des 3 derniers projets
      .toArray();

    //Serialization JSON
    projets = JSON.parse(JSON.stringify(projets));
    //Récupération des 3 premiers
    projets = projets.slice(0, 3);
  } catch (error) {
    console.log(error);
    projets = [];
  }

  return {
    props: {
      projets: projets,
      user: username,
    },
  };
}

// Ecrire le default de cette manière
// export default function Index(props) {
// STATES
// State 1 - Prix du BTC
// const [bitcoinPrice, setBitcoinPrice] = useState('En chargement');

// CYCLE DE VIE
// componentDidMount (Prix du BTC en temps réel)
// useEffect(() => {
//   fetch('https://blockchain.info/ticker')
//     .then((response) => response.json())
//     .then((data) => setBitcoinPrice(data.EUR.last))
//     .catch((error) => {
//       console.log(error);
//     });
// }, []);

// // PRE-RENDU : Invisible de l'utilisateur : sur le serveur
// export async function getStaticProps() {
//   // Challenge : afficher prix du BTC

//   // Etape 1 - Variable - Prix BTC en euros
//   let bitcoinPriceEnEuros;
//   // Etape 2 - fetch en AWAIT (fetch en coulisses)
//   await fetch('https://blockchain.info/ticker')
//     .then((response) => response.json())
//     .then((data) => (bitcoinPriceEnEuros = data.EUR.last))
//     .catch((error) => {
//       console.log(error);
//     });

//   // Etape 3 - Rendu JSX
//   return {
//     props: {
//       bitcoinPrice: bitcoinPriceEnEuros,
//     },
//   };
// }

 

- [...nextauth].js : 

// Librairies
import NextAuth from 'next-auth';
import CredentialsProvider from 'next-auth/providers/credentials';
import { connectToDataBase } from '../../../helpers/mongodb';
import { verifyPassword } from '../../../helpers/auth';

export const authOptions = {
  // session: {
  //   maxAge: 30 * 24 * 60 * 60, // 30 days
  //   generateSessionToken: () => {
  //     return randomUUID?.() ?? randomBytes(32).toString('hex');
  //   },
  // },

  // Ajout des informations de l'user (email, pseudo, password)
  providers: [
    CredentialsProvider({
      async authorize(credentials) {
        const { email, password } = credentials;

        // Connexion à MongoDB
        const clientMongoDB = await connectToDataBase();

        // Etape 1 - Utilisateur existant ?
        const user = await clientMongoDB
          .db()
          .collection('users')
          .findOne({ email: email });

        if (!user) {
          clientMongoDB.close();
          throw new Error(
            'Impossible de vous authentifier, merci de vérifier vos credentials.'
          );
        }

        // Etape 2 - Mot de passe est-il correct vs celui enregistré ?
        const isValid = await verifyPassword(password, user.password);

        if (!isValid) {
          clientMongoDB.close();
          throw new Error(
            'Impossible de vous authentifier, merci de vérifier vos credentials.'
          );
        }

        // Etape 3 - Succès
        clientMongoDB.close();
        return {
          email: user.email,
          name: user.pseudo,
          id: user._id,
          roles: user.roles,
        };
      },
    }),
  ],

  // Ajout de nouvelles informations (cookies) à notre session user
  callbacks: {
    // Récupération du token
    jwt: async (token, user) => {
      user && (token.user = user);
      return token;
    },
    // Récupération du user
    session: async (session, user) => {
      // Check d'existence de user
      if (user && user.user) {
        session.user = user.user;
      }
      return session;
    },
  },
};

export default NextAuth(authOptions);

 

 

Le message d'erreur est le suivant :

Server Error

TypeError: Cannot read properties of undefined (reading 'name')

This error happened while generating the page. Any console logs will be displayed in the terminal window.
Source

src\pages\index.js (194:28) @ name

  192 | 
  193 | if (session) {
> 194 |   username = session.user.name;
      |                          ^
  195 |   console.log(username);
  196 | }
  197 | 

 

De plus, le nom de l'utilisateur n'est pas affiché lors de la connexion.

Enfin, je ne peux plus lire les vidéos et accéder correctement à la formation alors que je suis abonné.

 

Comment est-il possible de résoudre les problemes svp ?

 

Merci par avance !

 

 

Image
Antoine Delamare
Le 22/03/2023 à 15:19

Probleme résolu ! Afin d'éviter le probleme, dans 'index.js', il faut modifier :

 

if (session) {
    username = session.user.name;
    console.log(username);
}

 

par :

 

if (session && session.user && session.user.name) {
    username = session.user.name;
    console.log(username);
}

 

 

Merci encore pour les retours !

Image
Louis-Nicolas Leuillet
Le 23/03/2023 à 12:03

Merci pour ton retour Antoine !

Image
Inscrivez-vous ou connectez-vous pour participer à la discussion.

Informations

Créée le March 21, 2023 9:41 PM
Dernière activité le March 21, 2023 9:41 PM