I am trying to implement authentication and user flow on my Nextjs website using auth0, but I am running into an issue with an error that I don’t understand. I am new to Nextjs so this project is somewhat of a learning project for me.
Tech stack used:
- Nextjs
- sst to host on was
- Auth0
Profile page form:
// src/app/profile/page.tsx
"use client";
import React, { useEffect, useState } from "react";
import { useUser, UserProfile } from "@auth0/nextjs-auth0/client";
import Image from "next/image";
import { CameraIcon } from "@heroicons/react/24/outline";
import axios, { AxiosError } from "axios";
type Props = {};
export default function Page({}: Props) {
const { user, isLoading } = useUser();
const [userInfo, setUserInfo] = useState<
| (UserProfile & {
username?: string;
family_name?: string;
phone_number?: string;
})
| undefined
>(undefined);
const [btnLoading, setBtnLoading] = useState(false);
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setUserInfo({ ...userInfo, name: e.target.value });
};
const handleFamilyNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setUserInfo({ ...userInfo, family_name: e.target.value });
};
const handleUsernameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
setUserInfo({ ...userInfo, username: e.target.value });
};
const handlePhoneNumberChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const phoneNumber = e.target.value;
if (phoneNumber.length > 1 && !phoneNumber.startsWith("+46")) {
setUserInfo({ ...userInfo, phone_number: `+46${phoneNumber}` });
} else {
setUserInfo({ ...userInfo, phone_number: phoneNumber });
}
};
const handleSave = async () => {
if (!userInfo) return;
const { sub, name, family_name, nickname } = userInfo;
setBtnLoading(true);
const response = await axios
.patch(`/api/profile?sub=${sub}`, {
name,
family_name,
nickname
})
.catch((error: AxiosError) => {
console.error("???? ~ handleSave ~ error:", error.response?.data);
});
console.log("???? ~ handleSave ~ response:", response);
setBtnLoading(false);
};
useEffect(() => {
setUserInfo(user);
}, [user]);
return (
<div className='flex flex-col justify-center items-center gap-5 px-2 md:px-0'>
{isLoading ? (
<div className='flex justify-center item-center w-screen h-screen'>
<span className='loading loading-infinity loading-lg text-secondary'></span>
</div>
) : (
<>
<h1 className='text-3xl font-bold'>Profil</h1>
<div className='avatar'>
<div className='w-40 mask mask-hexagon flex justify-center item-center bg-slate-950 group cursor-pointer'>
<Image
src={userInfo?.picture || ""}
width={64}
height={64}
alt='Profile picture'
className='group-hover:opacity-75 group-active:opacity-50 transition-opacity'
/>
<div className='relative z-50'>
<CameraIcon className='opacity-0 w-6 h-6 group-hover:opacity-100 transition-opacity' />
</div>
</div>
</div>
<label className='form-control w-full max-w-xs'>
<div className='label'>
<span className='label-text'>Mail</span>
</div>
<input
type='text'
placeholder='Type here'
className='input input-bordered w-full max-w-xs'
disabled
value={userInfo?.email || ""}
/>
</label>
<label className='form-control w-full max-w-xs'>
<div className='label'>
<span className='label-text'>Förnamn</span>
</div>
<input
type='text'
placeholder='Type here'
className='input input-bordered w-full max-w-xs'
onChange={handleNameChange}
value={userInfo?.name || ""}
/>
</label>
<label className='form-control w-full max-w-xs'>
<div className='label'>
<span className='label-text'>Efternamn</span>
</div>
<input
type='text'
placeholder='Type here'
className='input input-bordered w-full max-w-xs'
onChange={handleFamilyNameChange}
value={userInfo?.family_name || ""}
/>
</label>
<label className='form-control w-full max-w-xs'>
<div className='label'>
<span className='label-text'>Användernamn</span>
</div>
<input
type='text'
placeholder='Type here'
className='input input-bordered w-full max-w-xs'
onChange={handleUsernameChange}
value={userInfo?.username || ""}
/>
</label>
<label className='form-control w-full max-w-xs'>
<div className='label'>
<span className='label-text'>Mobil nummer</span>
</div>
<input
type='text'
placeholder='07XXXXXXXX'
className='input input-bordered w-full max-w-xs'
onChange={handlePhoneNumberChange}
value={userInfo?.phone_number || ""}
/>
</label>
<button
onClick={() => handleSave()}
className='btn btn-primary btn-xl w-full max-w-xs'
>
{btnLoading ? (
<span className='loading loading-infinity loading-lg'></span>
) : (
"Spara"
)}
</button>
</>
)}
</div>
);
}
profile route:
// src/app/api/profile/route.ts
import { Auth0Service } from "@/services/auth0-service";
import { streamRead as streamReader } from "@/utils/streamRead";
import { getQueryStringParamFromURL } from "@/utils";
export const PATCH = async (req: Request) => {
if (!req.body) {
return new Response("Missing body", { status: 400 });
}
const sub = getQueryStringParamFromURL("sub", req.url);
if (!sub) {
return new Response("User id missing", { status: 400 });
}
const user = await streamReader(req.body);
const userWithId = { ...user, sub: sub };
const updatedUser = await Auth0Service.handleUpdateUserInfo(userWithId);
return new Response(JSON.stringify({ updatedUser }), { status: 200 });
};
the auth0 service that should take care of sending the PATCH request to auth0:
// src/services/auth0-service.ts
import { auth0 } from "@/utils";
import { Auth0Config } from "@/utils/auth0/auth0-config";
import axios, { AxiosError } from "axios";
class Auth0ServiceImpl {
async handleUpdateUserInfo(user: any) {
const { sub, name, family_name, nickname } = user;
const response = await axios
.patch(
`https://[id].us.auth0.com/api/v2/users/${sub}`,
{
name,
family_name,
nickname
},
{
headers: {
Authorization: `Bearer ${Auth0Config.AUTH0_MANAGEMENT_API_TOKEN}`
}
}
)
.catch((error: AxiosError) => {
console.error("???? ~ handleSave ~ error:", error.response?.data);
});
return response;
}
}
export const Auth0Service = new Auth0ServiceImpl();
The error that I am getting:
⨯ node_modules/react-dom/cjs/react-dom-test-utils.development.js (1127:0) @ eval
⨯ TypeError: Cannot read properties of null (reading '0')
at eval (webpack-internal:///(rsc)/./node_modules/react-dom/cjs/react-dom-test-utils.development.js:1127:41)
at eval (webpack-internal:///(rsc)/./node_modules/react-dom/cjs/react-dom-test-utils.development.js:1740:5)
at (rsc)/./node_modules/react-dom/cjs/react-dom-test-utils.development.js (/Users/someuser/Documents/git/project/frontend/.next/server/vendor-chunks/react-dom.js:20:1)
at __webpack_require__ (/Users/someuser/Documents/git/project/frontend/.next/server/webpack-runtime.js:33:43)
at eval (webpack-internal:///(rsc)/./node_modules/react-dom/test-utils.js:4:20)
at (rsc)/./node_modules/react-dom/test-utils.js (/Users/someuser/Documents/git/project/frontend/.next/server/vendor-chunks/react-dom.js:30:1)
at __webpack_require__ (/Users/someuser/Documents/git/project/frontend/.next/server/webpack-runtime.js:33:43)
at eval (webpack-internal:///(rsc)/./node_modules/@testing-library/react/dist/@testing-library/react.esm.js:86:78)
at (rsc)/./node_modules/@testing-library/react/dist/@testing-library/react.esm.js (/Users/someuser/Documents/git/project/frontend/.next/server/vendor-chunks/@testing-library.js:1510:1)
at __webpack_require__ (/Users/someuser/Documents/git/project/frontend/.next/server/webpack-runtime.js:33:43)
at eval (webpack-internal:///(rsc)/./src/utils/testing/testing-utils.ts:6:80)
at (rsc)/./src/utils/testing/testing-utils.ts (/Users/someuser/Documents/git/project/frontend/.next/server/app/api/profile/route.js:388:1)
at __webpack_require__ (/Users/someuser/Documents/git/project/frontend/.next/server/webpack-runtime.js:33:43)
at eval (webpack-internal:///(rsc)/./src/utils/testing/index.ts:5:72)
at (rsc)/./src/utils/testing/index.ts (/Users/someuser/Documents/git/project/frontend/.next/server/app/api/profile/route.js:378:1)
at __webpack_require__ (/Users/someuser/Documents/git/project/frontend/.next/server/webpack-runtime.js:33:43)
at eval (webpack-internal:///(rsc)/./src/utils/index.ts:8:66)
at (rsc)/./src/utils/index.ts (/Users/someuser/Documents/git/project/frontend/.next/server/app/api/profile/route.js:358:1)
at __webpack_require__ (/Users/someuser/Documents/git/project/frontend/.next/server/webpack-runtime.js:33:43)
at eval (webpack-internal:///(rsc)/./src/app/api/profile/route.ts:7:64)
at (rsc)/./src/app/api/profile/route.ts (/Users/someuser/Documents/git/project/frontend/.next/server/app/api/profile/route.js:242:1)
at __webpack_require__ (/Users/someuser/Documents/git/project/frontend/.next/server/webpack-runtime.js:33:43)
at eval (webpack-internal:///(rsc)/./node_modules/next/dist/build/webpack/loaders/next-app-loader.js?name=app%2Fapi%2Fprofile%2Froute&page=%2Fapi%2Fprofile%2Froute&appPaths=&pagePath=private-next-app-dir%2Fapi%2Fprofile%2Froute.ts&appDir=%2FUsers%2Fsomeuser%2FDocuments%2Fgit%2Fproject%2Ffrontend%2Fsrc%2Fapp&pageExtensions=tsx&pageExtensions=ts&pageExtensions=jsx&pageExtensions=js&rootDir=%2FUsers%2Fsomeuser%2FDocuments%2Fgit%2Fproject%2Ffrontend&isDev=true&tsconfigPath=tsconfig.json&basePath=&assetPrefix=&nextConfigOutput=standalone&preferredRegion=&middlewareConfig=e30%3D!:17:139)