Где и как изменить объект пользователя сеанса после входа в систему?

У меня странная проблема, и я не знаю, в чем проблема. Я использую библиотеку next-auth, чтобы сделать систему аутентификации в моем Next.js приложение.

Все в порядке - могу войти, проверив, есть ли аккаунт в google firebase с предоставленными учетными данными, session создается правильно, но когда authorize обратный вызов инициализирован Я передаю данные, полученные от google firestore после правильного входа в систему. Пользовательский объект содержит все данные и передается вперед.

Затем, когда я захочу прочитать данные из session, некоторые данные, которые я передал ранее, отсутствуют.

Код:

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

export default (req, res) => 
    NextAuth(req, res, {
        providers: [
            Providers.Credentials({
                name: 'Credentials',
                credentials: {
                    phone: { label: "Phone number", type: "text" },
                    password: { label: "Password", type: "password" }
                },
                authorize: async (loginData) => {
                    const { csrfToken, phone, password } = loginData;
                    
                    //  checking if there is account with these credentials
                    let res = await login({
                        phone, 
                        password: sha1(md5(password)).toString()
                    })

                    //  200 = OK
                    if(res.status == 200){
                        //  collect account data
                        const user = {
                            phone,
                            ...res.data.info
                        }

                        //  user object is created correctly, whole data is stored there
                        console.log('received account data from firestore', user)
                        return Promise.resolve(user);
                    }
                    else {
                        //  wrong credentials
                        return Promise.resolve(null);
                    }
                }
            })
        ],
        callbacks: {
            session: async (session, user) => {
                console.log('data passed to object when signed in', user)
                //  user object there doesn't have all data passed before
                return Promise.resolve(session)
            }
        },
        debug: false
    })

Объекты, регистрируемые в консоли:

received account data from firestore 
{
  phone: '123123123',
  id: 'w2zh88BZzSv5BJeXZeZX',
  email: 'jan@gmail.com',
  name: 'Jan',
  surname: 'Kowalski'
}
data passed to object when signed in 
{
  name: 'Jan',
  email: 'jan@gmail.com',
  iat: 1603900133,
  exp: 1606492133
}

Лучше всего то, что объект (см. Выше) всегда имеет одни и те же свойства. Я могу передать любой объект в authorize обратный вызов, но в session, userобъект всегда имеет "name, email, iat, exp" ВСЕГДА. Единственное, что изменяется, - это значения этих двух свойств в объекте (имя, адрес электронной почты). (остальные свойства - "телефон, идентификатор, фамилия" - отсутствуют).

Ниже находится консоль в логе session объект в любом компоненте реакции:

import {
    signIn, 
    signOut,
    useSession
} from 'next-auth/client'

const [ session, loading ] = useSession();
console.log(session)

Фотография объекта консольного сеанса

Что я могу сделать? Должен ли я получать данные от firestore отдельно в sessionПерезвони? Рендеринг на стороне сервера Next.js вызывая проблему?

2 ответа

Я решил эту проблему сам.
Эта тема мне очень помогла!
https://github.com/nextauthjs/next-auth/issues/764

Ниже приводится объяснение:

callbacks: {
    jwt: async (token, user, account, profile, isNewUser) => {
        //  "user" parameter is the object received from "authorize"
        //  "token" is being send below to "session" callback...
        //  ...so we set "user" param of "token" to object from "authorize"...
        //  ...and return it...
        user && (token.user = user);
        return Promise.resolve(token)   // ...here
    },
    session: async (session, user, sessionToken) => {
        //  "session" is current session object
        //  below we set "user" param of "session" to value received from "jwt" callback
        session.user = user.user;
        return Promise.resolve(session)
    }
}

У меня тоже была эта проблема, и, как выяснилось, проблемы были вызваны функциями адаптера. В моем случае я хотел использовать v4 next-auth, и я хотел использовать DynamoDB. Поскольку официального адаптера v4 для Dynamo нет, мне пришлось написать свой собственный, основанный на общедоступном адаптере v3 .

Одна из функций, которые вы должны предоставить при создании собственного адаптера :

      async updateUser(user) {
  // use ddb client to update the user record
  // client returns `data`
  return { ...user, ...data.Attributes }
}

Оказывается, данные в data.Attributesэто то, что передается вашему обратному вызову как. Похоже, это отличается от реализации v3.

Поэтому в моем случае мне пришлось структурировать функцию адаптера Dynamodb, чтобы дать клиенту команду возвращать ALL_NEW, а не просто UPDATED_NEW (что и делает адаптер v3).

Моя полная updateUser()функция выглядит следующим образом - имейте в виду, что на этом этапе я использовал рекомендованную структуру таблицы Dynamodb (с которой я не обязательно согласен, особенно в моем случае использования, но это другая история)

      async updateUser(user) {
    const now = new Date()
    const data = await client.update({
        TableName: tableName,
        Key: {
            pk: `USER#${user.id}`,
            sk: `USER#${user.id}`,
        },
        UpdateExpression:
            "set #emailVerified = :emailVerified, #updatedAt = :updatedAt",
        ExpressionAttributeNames: {
            "#emailVerified": "emailVerified",
            "#updatedAt": "updatedAt",
        },
        ExpressionAttributeValues: {
            ":emailVerified": user.emailVerified?.toISOString() ?? null,
            ":updatedAt": now.toISOString(),
        },
        ReturnValues: "ALL_NEW",
    }).promise()
    
    return { ...user, ...data.Attributes }
},

В результате я вижу user полностью заселен в jwt() Перезвоните:

      callbacks: {
    async jwt({ token, user, account, profile, isNewUser }) {
        console.debug("callback jwt user:", user)
        user && (token.user = user)
        return token
    },

Выходные данные отладки - это полная запись для пользователя из DynamoDB, и ее можно использовать, как описано в ответе @MateuszWawrzynski.

Другие вопросы по тегам