Next.js-Auth0:更新user_metadata后无需登出刷新用户会话
问题
在Next.js集成Auth0时,更新user_metadata后无法实时获取用户最新数据,必须登出再登录才能看到变化。相关代码如下:
前端页面(index.tsx)
// index.tsx export default function home({user_data, some_data}) { const [selected, setSelect] = useState(null) async function handleAddToFavourite() { if (selected) { const data = await axios.patch("api/updateMetadata", {some_favorite: selected.id}) // Errorhandling ... } } return ( <div> <SearchData setData={setSelect} data={some_data}/> <Button onClick={handleAddToFavorite}>Add to Favorite</Button> <div>Selected: {selected.id}</div> <div>My Favorite: {user_data.user_metadata.some_favorite}</div> </div> ) } export const getServerSideProps = withPageAuthRequired({ returnTo: "/foo", async getServerSideProps(ctx) { const session = await getSession(ctx.req, ctx.res) const {data} = await axios.get("https://somedata.com/api") return {props: {some_data: data, user_data: session.user}} })
后端接口(api/updateMetadata.ts)
// api/updateMetadata.ts async function handler(req: NextApiRequest, res: NextApiResponse) { const session = await getSession(req, res); if (!session || session === undefined || session === null) { return res.status(401).end(); } const id = session?.user?.sub; const { accessToken } = session; const currentUserManagementClient = new ManagementClient({ token: accessToken, domain: auth0_domain.replace('https://', ''), scope: process.env.AUTH0_SCOPE, }); const user = await currentUserManagementClient.updateUserMetadata({ id }, req.body); return res.status(200).json(user); } export default withApiAuthRequired(handler);
Auth0配置(pages/api/auth/[...auth0].tsx)
// pages/api/auth/[...auth0].tsx export default handleAuth({ async profile(req, res) { try { await handleProfile(req, res, { refetch: true, }); } catch (error: any) { res.status(error.status || 500).end(error.message); } }, async login(req, res) { try { await handleLogin(req, res, { authorizationParams: { audience: `${process.env.AUTH0_ISSUER_BASE_URL}/api/v2/`, scope: process.env.AUTH0_SCOPE, }, }); } catch (error: any) { res.status(error.status || 400).end(error.message); } }, });
已尝试的无效方案
- 使用客户端函数
useUser(),返回数据与服务端getSession()一致,无更新 - 在
updateMetadata接口末尾添加updateSession(req, res, session) - 在Auth0登录流中添加PostLogin Action(存在语法错误):
// Auth0 action flow - login exports.onExecutePostLogin = async (event, api) => { const namespace = 'https://example.com'; const { some_favorite } = event.user.user_metadata; if (event.authorization) { // Set claims api.idToken.setCustomClaim(`${namespace}/some_favorite`, ); } };
可行解决方案及错误分析
1. 修复PostLogin Action的语法与逻辑错误
原Action缺少赋值变量,且未同步更新Access Token,修改后:
exports.onExecutePostLogin = async (event, api) => { const namespace = 'https://example.com'; const { some_favorite } = event.user.user_metadata; if (event.authorization && some_favorite) { // 同时更新ID Token和Access Token,确保前后端都能获取最新值 api.idToken.setCustomClaim(`${namespace}/some_favorite`, some_favorite); api.accessToken.setCustomClaim(`${namespace}/some_favorite`, some_favorite); } };
2. 更新后端接口,同步会话数据
更新user_metadata后,需重新从Auth0拉取最新用户信息并更新会话,不能直接使用旧的session对象:
// api/updateMetadata.ts async function handler(req: NextApiRequest, res: NextApiResponse) { const session = await getSession(req, res); if (!session) { return res.status(401).end(); } const id = session?.user?.sub; const { accessToken } = session; const currentUserManagementClient = new ManagementClient({ token: accessToken, domain: auth0_domain.replace('https://', ''), scope: process.env.AUTH0_SCOPE, }); // 更新用户元数据 const updatedUser = await currentUserManagementClient.updateUserMetadata({ id }, req.body); // 重新获取完整的最新用户信息 const freshUser = await currentUserManagementClient.getUser({ id }); // 更新会话中的用户数据 session.user = { ...session.user, ...freshUser }; await updateSession(req, res, session); return res.status(200).json(updatedUser); } export default withApiAuthRequired(handler);
3. 前端主动刷新状态或用户数据
调用更新接口成功后,通过两种方式同步前端显示:
- 方式一:用接口返回的更新数据直接修改本地状态
export default function home({user_data, some_data}) { const [selected, setSelect] = useState(null) const [favorite, setFavorite] = useState(user_data.user_metadata.some_favorite) async function handleAddToFavourite() { if (selected) { const { data: updatedUser } = await axios.patch("api/updateMetadata", {some_favorite: selected.id}) // 直接更新本地状态 setFavorite(updatedUser.user_metadata.some_favorite) } } return ( <div> <SearchData setData={setSelect} data={some_data}/> <Button onClick={handleAddToFavorite}>Add to Favorite</Button> <div>Selected: {selected?.id}</div> <div>My Favorite: {favorite}</div> </div> ) }
- 方式二:调用Auth0的refresh方法刷新用户会话
import { useUser } from '@auth0/nextjs-auth0'; export default function home({user_data, some_data}) { const { user, refresh } = useUser(); const [selected, setSelect] = useState(null) const [favorite, setFavorite] = useState(user?.user_metadata?.some_favorite) async function handleAddToFavourite() { if (selected) { await axios.patch("api/updateMetadata", {some_favorite: selected.id}) // 刷新用户会话,拉取最新数据 await refresh(); // 更新本地状态 setFavorite(user.user_metadata.some_favorite) } } return ( <div> <SearchData setData={setSelect} data={some_data}/> <Button onClick={handleAddToFavorite}>Add to Favorite</Button> <div>Selected: {selected?.id}</div> <div>My Favorite: {favorite}</div> </div> ) }
4. 确认handleProfile配置有效性
你当前在[...auth0].tsx中设置的refetch: true是正确的,确保调用/api/auth/me时会从Auth0重新拉取最新用户数据,无需修改。
内容的提问来源于stack exchange,提问作者Aleksander Eriksen
相关产品推荐
相关产品推荐

