在登录后如何更改会话用户对象以及更改的位置和方式?

28
我有一个问题,但不确定问题的来源。在我的Next.js应用中使用next-auth库来创建认证系统。

一切都很好-我可以通过检查是否有带有提交凭据的帐户在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回调中传递任何对象,但在sessionuser对象中,始终具有“name,email,iat,exp”属性。唯一变化的是对象中这两个属性的值(即name,email)。 (其余属性 - “phone,id,surname”-缺失)。

下面是在任何React组件中记录在控制台的session对象:

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

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

控制台记录的会话对象照片

我该怎么办?我是否需要在session回调函数中单独从firestore接收数据?是Next.js的服务器端渲染导致了问题吗?


1
遇到了同样的问题,在这个问题上有一些额外的评论:https://github.com/nextauthjs/next-auth/issues/312#issuecomment-718786847 - DeBraid
2个回答

82

我自己解决了那个问题。
这个问题的讨论帖对我帮助很大!
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)
    }
}

编辑:由于NextAuth更新为v4

NextAuth的第4版对以上示例中的回调函数进行了一些更改。现在只分配一个参数到jwtsession函数中。但是,你可以解构它以分离变量。其余代码与之前相同。

https://next-auth.js.org/configuration/callbacks#jwt-callback
https://next-auth.js.org/configuration/callbacks#session-callback

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

...
callbacks: {
    jwt: async ({ token, user }) => {
        user && (token.user = user)
        return token
    },
    session: async ({ session, token }) => {
        session.user = token.user
        return session
    }
}
...

3
谢谢,这是一个不错的线索。如今签名看起来更像是 session: async (session, user) => { }。另外,由于您正在使用 async,您只需要返回 session,而无需使用 Promise.resolve - ABCD.ca
1
这也解决了我的问题,感谢v4的编辑。 - Richard Vartan Melkonian
要附加到令牌和会话对象的属性必须是“user”而已。我浪费了很多时间尝试给它命名其他东西。最终通过这个答案使其工作。谢谢! - Chayanin
我一直在为用户名这个问题苦苦挣扎。非常感谢您分享您的解决方案! - Stephen Scott Moore
你是如何将用户字段保持在会话中的?如果会话回调再次运行,新字段将不会包含在内。 - Dr J
显示剩余3条评论

0
我也遇到了这个问题,后来发现是 Adapter 中的函数引起的问题。在我的情况下,我想使用 v4 的 next-auth,并且我想使用 DynamoDB。由于没有官方的 DynamoDB v4 适配器,所以我不得不自己编写代码,基于 公开可用的 v3 适配器 进行修改。
创建自己的适配器时,必须提供其中一个函数 是:
async updateUser(user) {
  // use ddb client to update the user record
  // client returns `data`
  return { ...user, ...data.Attributes }
}

原来在 data.Attributes 中的数据会作为 jwt() 回调函数user 参数传递。这似乎与 v3 实现不同。
因此,在我的情况下,我必须构造 dynamodb 适配器的 updateUser() 函数,以指示客户端返回 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 }
},

因此,在 jwt() 回调函数中,我可以看到 user 已经被完全填充:
callbacks: {
    async jwt({ token, user, account, profile, isNewUser }) {
        console.debug("callback jwt user:", user)
        user && (token.user = user)
        return token
    },

调试输出是从DynamoDB中获取的用户完整记录,可以按照@MateuszWawrzynski的答案所述使用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接