I am working on a messages notification system for my app. I built a ChatContext provider file which wraps the app and binds to pusher if someone is logged in on page load.
Every user will have their own private channel (private-unread-${session.user.id}
) and I need some way to control that only the logged in user can connect to this channel.
ISSUE: I keep getting the following error when trying to send auth data to my Next.js POST endpoint ('/pusher/auth'). Using 'jsonp' instead and changing my API to GET has it's own set of random issue so i'd prefer to just stuck with ajax if possible.
SyntaxError: Unexpected token 's', "socket_id="... is not valid JSON at JSON.parse () at parseJSONFromBytes (node:internal/deps/undici/undici:5329:19) at successSteps (node:internal/deps/undici/undici:5300:27) at fullyReadBody (node:internal/deps/undici/undici:1447:9) at process.processTicksAndRejections (node:internal/process/task_queues:95:5) at async specConsumeBody (node:internal/deps/undici/undici:5309:7) at async POST (webpack-internal:///(rsc)/./app/api/pusher/auth/route.js:20:34)
Client Side Context:
'use client'
import React, { createContext, useState, useEffect } from 'react';
import { useSession } from 'next-auth/react';
import PusherClient from 'pusher-js';
const ChatContext = createContext();
const ChatProvider = ({ children }) => {
const { data: session } = useSession();
const [unreadChats, setUnreadChats] = useState([]);
const [pusherConnected, setPusherConnected] = useState(false);
useEffect(() => {
if (session) {
fetchUnreadMessages();
}
}, [session]);
useEffect(() => {
if (session && !pusherConnected) {
const pusher = new PusherClient(process.env.NEXT_PUBLIC_PUSHER_APP_KEY, {
cluster: 'us2',
encrypted: true,
channelAuthorization: {
endpoint: '/api/pusher/auth',
params: {
channel_name: `private-unread-${session.user.id}`,
},
},
});
const channel = pusher.subscribe(`private-unread-${session.user.id}`);
channel.bind('message:new', function (data) {
console.log(data);
setMessages(currentState => [data.message, ...currentState])
});
setPusherConnected(true);
return () => {
channel.unbind('message:new')
};
}
}, [session])
return (
<ChatContext.Provider value={{ unreadChats }}>
{children}
</ChatContext.Provider>
);
};
export { ChatProvider, ChatContext };
Auth Route:
import PusherServer from 'pusher';
const pusherServer = new PusherServer({
appId: process.env.PUSHER_APP_ID,
key: process.env.NEXT_PUBLIC_PUSHER_APP_KEY,
secret: process.env.PUSHER_SECRET,
cluster: 'us2',
useTLS: true
});
export const POST = async (req) => {
try {
const { socket_id, channel_name } = await req.json();
const token = **user's id pulled here from next auth**
// Verify that the channel_name matches the user's ID (assuming channel_name is of the format `private-unread-<user_id>`)
if (!channel_name.endsWith(token.sub)) {
return new Response(JSON.stringify({ error: "Failed to auth pusher" }), { status: 403 });
}
const auth = pusherServer.authorizeChannel(socket_id, channel_name)
return new Response(JSON.stringify(auth), {
status: 200,
headers: { 'Content-Type': 'application/json' }
});
} catch (error) {
console.log(error);
return new Response(JSON.stringify({ error: "Failed to auth pusher" }), { status: 403 });
}
};
try to use the formdata of the request. I just had the same and saw that it is not send as json but as form values.
const requestData = await req.formData();
const socketId = requestData.get('socket_id') as string;
const channel = requestData.get('channel_name') as string;