NextAuth throwing CLIENT_FETCH_ERROR error in session callback - javascript

Having some trouble with setting up nextauth v4. Getting this error:
Client fetch error, Unexpected end of JSON input {error: {…}, path:
'session', message: 'JSON.parse: unexpected end of data at line 1
column 1 of the JSON data'}.
To fix it they say you have to add the url path to a .env file when deploying. I’m working on localhost so this shouldn't be a problem, but after adding it, still the same error.
When I comment out the async session callback in [...nextauth] file, the error doesn’t pop up and the session is “authenticated” but doesn’t persist. I’ve been staring it at this for a good while now and could use some help!
[...nextauth].js
import NextAuth from "next-auth";
import CredentialsProvider from "next-auth/providers/credentials";
import { PrismaClient } from "#prisma/client";
const prisma = new PrismaClient();
export default NextAuth({
providers: [
CredentialsProvider({
async authorize(credentials, res) {
//find existing user
const user = await prisma.user.findUnique({
where: {
email: credentials.email,
},
});
if (
credentials.email === user.email &&
credentials.password === user.password
) {
return user;
} else {
return res.status(409).send({ message: "No user with that email." });
}
},
}),
],
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id;
return token;
}
},
//commenting this function and no error shows up
async session({ session, token }) {
if (token) {
session.id = token.id;
return session;
}
},
},
secret: "mysecret",
jwt: {
secret: "mysecret",
encryption: true,
},
session: {
strategy: "jwt",
maxAge: 1 * 24 * 60 * 60,
},
});
auth-form
import { signIn, useSession } from "next-auth/react";
export default function AuthForm() {
const { data: session } = useSession();
const handleSubmit = async (userData) => {
const { error, ok, url } = await signIn("credentials", {
redirect: false,
email: userData.email,
password: userData.password,
callbackUrl: "/profiel",
});
if (ok) {
router.push(url);
} else {
alert(error);
}
};
return (
<Form onSubmit={handleSubmit}>
<Field id="email" />
<Field id="password" />
<button type="submit">{isRegistered ? "Sign in" : "Register"}</button>
</Form>
);
}
_app.js
import { SessionProvider } from "next-auth/react";
function MyApp({ Component, pageProps: { session, ...pageProps } }) {
return (
<SessionProvider session={session}>
<Component {...pageProps} />
</SessionProvider>
);
}

The session and jwt callbacks need to return a session and jwt object respectively. You need to move the return statements in each function after the if block.
callbacks: {
async jwt({ token, user }) {
if (user) {
token.id = user.id;
}
return token;
},
async session({ session, token }) {
if (token) {
session.id = token.id;
}
return session;
}
}

Related

next-auth/discord callbacks arent modifying data

I'm using next-auth/discord however when using the session callback to set a user id to the session it does not set the property.
[...nextauth].js
import NextAuth from "next-auth/next";
import DiscordProvider from "next-auth/providers/discord";
export default NextAuth({
providers: [
DiscordProvider({
...
session: {
strategy: "jwt",
...
},
callbacks: {
async session({ session, user }) {
session.user.id = user.id;
return session;
}
}
})
]
});
/api/page.js
import { getSession } from 'next-auth/react';
export default async function handler(req, res) {
const session = await getSession({ req });
console.log(session);
}
This logs:
{
user: {
name: ...,
email: ...,
image: ...
},
expires: ...
}
With no user.id property.
Fixed it, callbacks should have been in NextAuth object. Also callbacks shouldve been:
async jwt({ token, user }) {
if (user) {
token.id = user.id
}
return token
},
async session({ session, token }) {
session.user.id = token.id
return session
}

Next auth session disappearing data in development environment

I am using next auth in my next.js project,
but during development, if the server refreshes then the sessio immediately loses the data in it, also if I change to another tab in the browser and then return back to it, the session loses its data, and I will be forced to sign out then sign in to let it work again.
This doesn't happen in production mode.
below it the [...nextauth].js file code:
import NextAuth from "next-auth/next";
import GoogleProvider from "next-auth/providers/google";
import CredentialsProvider from "next-auth/providers/credentials";
import axios from "axios";
let userInfo = {};
export default NextAuth({
site: process.env.SITE,
providers: [
CredentialsProvider({
name: "Credentials",
async authorize(credentials, req) {
let status;
let user = {};
let message = ''
await axios
.post(
`${process.env.NEXT_PUBLIC_DOMAIN_URL}/app/auth`,
{ userName: credentials.username.replace(/\s/g, ''), pwd: credentials.password },
{
headers: { "Content-Type": "application/json" },
withCredentials: true,
}
)
.then(function (response) {
if (response?.data) {
user = { ...response.data };
userInfo = user;
}
})
.catch((error) => {
if (error.response) {
throw new Error(JSON.stringify({ errors: error?.response?.data?.message, status: false }))
} else if (error.request) {
throw new Error(JSON.stringify({ errors: 'Server error, please try again.', status: false }))
} else {
throw new Error(JSON.stringify({ errors: 'Network error, please try again.', status: false }))
}
});
return user;
},
}),
],
pages: {
signIn: "/Auth/Login",
},
session: {
jwt: true,
strategy: "jwt"
},
jwt: {
maxAge: 60 * 60 * 24 * 30 * 6,
},
secret: process.env.NEXT_AUTH_SECRET,
callbacks: {
async jwt({ token, user, account, profile, isNewUser }) {
user && (token = { ...token, ...user });
return token;
},
async session({ session, token }) {
if (token) {
session.user = { ...userInfo };
}
return session;
},
},
});
Any help please?

NextAuth v4 Credentials Provider not returning Id

I am trying to learn next-auth, I finally got the login to work but when I pass the id from the user in the database it doesn't show up in the session only the values that I provided null to show up
import NextAuth from 'next-auth'
import CredentialProvider from 'next-auth/providers/credentials'
import connectMongo from '../../../lib/db'
import User from '../../../models/userModel'
export const authOptions = {
session: {
strategy: 'jwt',
},
providers: [
CredentialProvider({
async authorize(credentials) {
await connectMongo()
const u = await User.findOne({ username: credentials.username })
console.log(u.id)
if (u) {
return {
id: u.id,
name: null,
email: null,
image: null,
}
}
// login failed
return null
},
}),
],
}
export default NextAuth(authOptions)
login is successful with no errors but the id doesn't show up in the props.
{email: null, image: null, name: null} is all that is returned.
If I don't add null to all those keys it returns this error SerializableError: Error serializing .session.user.name returned from getServerSideProps in "/authenticated".
Reason: undefined cannot be serialized as JSON. Please use null or omit this value.
import { authOptions } from './api/auth/[...nextauth]'
import { unstable_getServerSession } from 'next-auth/next'
function Authenticated(props) {
return <div>Authenticated</div>
}
export default Authenticated
export async function getServerSideProps(context) {
const session = await unstable_getServerSession(
context.req,
context.res,
authOptions
)
if (!session) {
return {
redirect: {
destination: '/',
permanent: false,
},
}
}
return {
props: {
session,
},
}
}
get session in getServerSideProps also returns an empty object after successful login with no errors
import { getSession } from 'next-auth/react'
function Authenticated(props) {
console.log(props)
return <div>Authenticated</div>
}
export default Authenticated
export async function getServerSideProps(context) {
const session = await getSession({ req: context.req })
if (!session) {
return {
redirect: {
destination: '/',
permanent: false,
},
}
}
return {
props: { session },
}
}

How to render data from parse in next-auth

I want to render data in client side below from parse after signin from next auth using useSession
{
objectId: 'nttMcKIOQJ',
username: 'fiik346',
email: 'fiik346#gmail.com',
createdAt: '2022-06-26T07:56:41.888Z',
updatedAt: '2022-06-26T07:56:41.888Z',
ACL: { '*': { read: true }, nttMcKIOQJ: { read: true, write: true } },
sessionToken: 'r:2f2c569be3b5120f3893834c2ace7b67'
}
But it's just render email
{"user": {"email":"fiik346#gmail.com"},"expires":"2022-07-26T09:01:41.163Z"}
"authenticated"
This is my callbacks code
// /pages/api/auth/[...nextauth].js
...
callbacks: {
async session({ session, token, user }) { // Send properties to the client, like an access_token from a provider. return session
}, async jwt({ token, user, account, profile, isNewUser }) { return token
} },
...
And client side code
import { useSession } from 'next-auth/react'
import { getToken } from 'next-auth/jwt'
export default function accountIndex() {
const {data: session, status} = useSession() return ( <div>
<h1>Account is {status}</h1>
<pre className="overflow-auto">
{JSON.stringify(session)}
<br/>
{JSON.stringify(status)}
</pre>
</div>
)
}
I don't know how to change my callbacks code to display data from database
callbacks: {
jwt: async ({token, user}) => {
if (user) {
token.data = user
}
return token
},
session: async ({session, token}) => {
if (token.data) {
session.user = token.data
}
return session
}
}
After a while and try harder, I was successful to render data. Just change callback code like below.
...
callbacks: {
async session({ session, token, user }) {
// Send properties to the client, like an access_token from a provider.
session.accessToken = token.accessToken
session.user = token.data
return session
},
async jwt({ token, user, account, profile, isNewUser }) {
if (user) {
token.data = user
}
return token
}
},
...

Problem using Next-Auth with Credentials Provider for authenticating on existing system

I am using Next-Auth Credentials provider to authenticate using our existing API.
When I follow the directions on https://next-auth.js.org/configuration/callbacks
like this:
callbacks: {
async jwt({ token, user }) {
if (user) {
token.accessToken = user.jwt
}
return token
},
async session({ session, token, user }) {
session.accessToken = token.accessToken
return session
}
}
the resulting session object from useSession() looks like this:
{
expires: "2022-03-22T18:29:02.799Z",
user: {email: 'john#nextIsGreat.com'}
}
I can't use that as it does not have the token available.
So I was able to make up my own working solution, but it is kind of strange because of the way things are grouped together. Here is what I am doing now, that I am trying to figure out how to do better. I use comments to point out the problem areas:
[...nextauth].js:
import NextAuth from 'next-auth'
import Credentials from 'next-auth/providers/credentials'
import axios from 'axios'
export default NextAuth({
providers: [
Credentials({
name: 'Email and Password',
credentials: {
username: { label: 'Username', type: 'text', placeholder: 'jsmith' },
password: { label: 'Password', type: 'password' }
},
authorize: async (credentials) => {
const url = process.env.API_URL + '/authenticate'
const result = await axios.post(url, {
username: credentials.username,
password: credentials.password
})
const user = result.data
console.log(user)
//It logs this:
/*
{
jwt: 'eyJhbasU1OTJ9.NQ356H4Odya62KmN...', //<---***This is the token i pass in to all of my API calls****
user: {
userId: 207,
email: 'john#nextIsGreat.com',
firstName: 'John',
lastName: 'Doe',
roleId: 1,
}
}
*/
if (user) {
return Promise.resolve(user)
} else {
return Promise.resolve(null)
}
}
})
],
callbacks: {
async jwt({ token, user }) {
if (user) {
if (user.jwt) {
token = { accessToken: user.jwt, restOfUser: user.user }
}
}
return token
},
async session(seshProps) {
return seshProps
}
}
})
Home.js:
export const Home = () => {
const { data: session } = useSession()
console.log(session)
//LOGS THIS --->
/*
{
"session": { "user":{}, "expires":"2022-03-22T17:06:26.937Z"},
"token":{
"accessToken":"eyJ...",
"iat":1645376785,
"exp":1647968785,
"jti":"41636a35-7b9a-42fd-8ded-d3dfgh123455a"
"restOfUser": {
"userId":207,
"email":"john#nextIsGreat.com",
"firstName":"John",
"lastName":"Doe",
"roleId":1
}
}
{
*/
const getPosts=()=> {
const url = 'localhost:4000/posts'
const {data} = axios.get(url, {
Authorization: session.token.accessToken <--**This is the way I am calling my API
})
console.log(data)
}
return (
<div onClick={getPosts}>
Hello, {session.token.restOfUser.firstName}
/* I have to access it like this now, which seems wrong ***** */
</div>
)
}
Cheers for creating your own solution but you do not need it. NextAuth CredentialsProvider handles it already by setting your NextAuth session configuration to session: {strategy: "jwt", ... }.
You can also remove your callbacks for jwt() and session() and remove your owned generated JWT access token. As you do not need it, this way you can authenticate your existing system.
And at your CredentialsProvider({authorize(){}} authorize method. If you had directly connected to the user database, you can directly look up the user credential without doing a post request since it is already considered a server-side function.

Categories

Resources