Hello guys, I have a question about fetching user data from a custom endpoint.
I'm making a website that uses payloadcms as backend. I have kept the default authentication method, which I believe uses JWT.
After the user successfully signs in in the front-end with this
const response = await axios.post('/backend/api/users/login', credentials)
he gets assigned a cookie, which helps me keep the user session alive.
The problem is that I'm trying to make the user send a request like this
const reqFinishedSimulation = axios.post(
'/backend/api/simulations-history/v1/correction',
finishedSimulationReduxState,
{ withCredentials: true },
)
It has
withCredentials
set to true, which means that it will send the user cookie in the headers.
The link
/backend/api/simulations-history/v1/correction
corresponds to a custom endpoint in payloadcms
endpoints: [
{
path: '/v1/correction',
method: 'post',
handler: async (req, res, next) => {
}
}
]
From this endpoint, I want to get the userID from the
req
. I can't submit the userID inside the request, because that would mean that if a user is logged in, the endpoint will accept any userID parameter, and a malicious user could change the userID in the request and submit ids of other users.
Is it possible to get the userID from a
req
object in a custom endpoint?
Thank you
The request object should have a user object on
req.user
Is this not present?
Unfortunately it is not present, I have searched online and done some tests before posting the question here π¦
All I was able to find in the
req
object is the cookie and the payload jwt token
Correction: only the cookie, the token is already inside the cookie
So when you make a request to a payload-managed endpoint (eg youre not circumventing payload via express directly) it passes through the auth middleware and the user is added to the request context
Wait I'll try to make a request to a payload managed endpoint with the same axios request that uses
withCredentials
to see if it submits the user object in the request
I'm trying to think of what it could be, if you inspect the fetch in your frontend
Yeah that's what im thinking...axios isnt sending the cookie for w/e reason
No no, the cookie is being sent, but I don't know how to handle the cookie in the custom endpoint to get the userID directly in the backend
You dont need to handle the cookie, it's automatically handled for you and the relevant user is added to the req object
Oh, then just a second that I try to check a request to a payload managed endpoint
Yeah good idea! try the
me
endpoint
I actually tried it before, and it attaches the
user
object to the request, but I thought that that endpoint works differently
It was not an axios request tho, I just went here directly from the browser that had the cookie for the logged in user
the
req.user
object is only missing if the request couldn't be authenticated
hence you can use it as a check to limit access on your endpoint
try a straight fetch without axios
const form = new FormData()
form.append('unset', 'true')
const options: RequestInit = {
method: 'POST',
body: form,
credentials: 'include',
}
await fetch(`url`, options)
excuse the formatting
Okay, just 1 sec
^ this is what i used for one of my custom endpoints so ik it works
and even better in a try/catch π
i handled my error with a catch trail π€
Fair fair β€οΈ
I did it, it has sent the cookie, but still no
user
object in the request
are you sending this from localhost to localhost or is this on a server?
from localhost to localhost
To be precise, the server is on a virtualmachine, and I forwarded the ports so I can connect to the server using
localhost:3000
(fontend) and
localhost:3001
(backend)
and is the
/me
endpoint returning just fine or does that not work as well?
try {
const req = await fetch('{cms-url}/api/{user-collection}/me', {
method: "GET",
credentials: "include",
headers: {
"Content-Type": "application/json",
},
})
const data = await req.json()
} catch (err) {
console.log(err)
}
like this
Also are your cors/csrf configs set?
Not sure if that's relevant here
the
/me
works as expected. I have just checked the requests in the browser and hte
user
is included
Oo nice
is killin it π
{
"user": {
"id": "63e1fae9af38ad9ec568f1e1",
"firstName": "John",
"lastName": "Wick",
"instagram": "@abcdefghi",
"phone": 44447765,
"birthday": "1970-06-24T00:00:00.000Z",
"gender": "1",
"email": "dev@email.com",
"createdAt": "2023-02-07T07:16:57.806Z",
"updatedAt": "2023-06-13T15:42:39.632Z",
"referral": {},
"_strategy": "local-jwt"
},
"collection": "users",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImRldkBlbWFpbC5jb20iLCJpZCI6IjYzZTFmYWU5YWYzOGFkOWVjNTY4ZjFlMSIsImNvbGxlY3Rpb24iOiJ1c2VycyIsImlhdCI6MTY4NjY3MDk1OSwiZXhwIjoxNzU4NjcwOTU5fQ.jfQY_rPK3bHZ1zTPycrMsPghzV9xj3ctaLg2H_ZU-go",
"exp": 1758670959
}
This is the preview of the request to
/me
It has everything in it
wait that's the response right?
{
"user": {
"id": "63e1fae9af38ad9ec568f1e1",
"firstName": "John",
"lastName": "Wick",
"instagram": "@abcdefghi",
"phone": 44447765,
"birthday": "1970-06-24T00:00:00.000Z",
"gender": "1",
"email": "dev@email.com",
"createdAt": "2023-02-07T07:16:57.806Z",
"updatedAt": "2023-06-13T15:42:39.632Z",
"referral": {},
"_strategy": "local-jwt"
},
"collection": "users",
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6ImRldkBlbWFpbC5jb20iLCJpZCI6IjYzZTFmYWU5YWYzOGFkOWVjNTY4ZjFlMSIsImNvbGxlY3Rpb24iOiJ1c2VycyIsImlhdCI6MTY4NjY3MDk1OSwiZXhwIjoxNzU4NjcwOTU5fQ.jfQY_rPK3bHZ1zTPycrMsPghzV9xj3ctaLg2H_ZU-go",
"exp": 1758670959
}
this is the response to
/me
request
Ah okay cool
Yeah but in your own custom handler the
req
object doesnt have a user?
like
req.user
is just undefined?
Exactly, it's undefined
All I was able to get in the
req
is the cookie, but no user object
Did you call payload.authenticate() ?
Not sure if this is localAPI or not
No you dont need to do that typically
It passes through a middleware
Woah really?
I have tried to console log it but it just said
function
I was not sure how that worked
Yeah you wouldnt log it
But
@858693520012476436are you sure that calling payload.authenticate() isnt necessry for their situation?
I'm interested in seeing their payload.config.js
Well not exactly but I wanna get there first, all custom endpoints that dont circumvent payload pass through the necessary middleware for auth
Ahhh okay, I dont mean to butt in, you got this π
is this endpoint on a collection?
Yes, it's called SimulationsHistory, I added it to payload.config
import { CollectionConfig } from 'payload/types'
import payload from 'payload'
import axios from 'axios'
import fs from 'fs'
import type { Solution, FinishedSimulation } from '../custom-types'
const SimulationsHistory: CollectionConfig = {
slug: 'simulations-history',
admin: {
useAsTitle: 'id',
group: 'Simulations Editor',
},
labels: { singular: 'Simulation History', plural: 'Simulations History' },
access: {
read: () => true,
create: () => true,
update: () => true,
delete: () => true,
},
endpoints: [
{
// This is the endpoint that will receive the finished simulation, correct it, and either return an error or create a new record in the simulations history
// The sections and the questions should keep their original order inside the simulation history
path: '/v1/correction',
method: 'post',
handler: async (req, res, next) => {
const simulation: FinishedSimulation = req.body
console.log(req)
},
},
],
fields: [
{
type: 'text',
name: 'title',
},
],
}
export default SimulationsHistory
It doesn't have any fields because I wanted to solve the issue with not finding the user first
This is on the latest payload right?
Just covering the bases here π
"node_modules/payload": {
"version": "1.8.3",
I updated it quite recently
not quite the latest but it should be fine, i'd do a
yarn upgrade
anyway just to be safe
Wanna try adding your endpoint to the payload config instead? So it's not attached to your collection, my working examples are all built that way so I wanna see,
I'm not sure how to do that..
We are talking about
payload.config.ts
that is inside the root directory right?
src directory**
The payload.config.ts has the config object that also accepts 'endpoints;'
Yes you can add a new key for
endpoints
which is an array of endpoints similar to the one you wrote
the difference will be that the path wont be hoisted to the collection
so it will be
backend/api/PATH
i just wanna see if the
req.user
is being correctly added there
export default buildConfig({
serverURL: 'http://localhost:3001',
admin: {
user: Admins.slug,
},
endpoints: [
{
path: '/v1/pleasework',
method: 'get',
handler: async (req, res, next) => {
console.log(req)
}
}
],
collections: [
Admins,
Users,
Courses,
Lessons,
Exercises,
Questions,
QuestionsMedia,
ManualSimulations,
UserStatistics,
// GeneratorCourses,
// GeneratorSubjects,
// GeneratorTopics,
// Generators,
QuestionsHistory,
GeneratedSimulations,
SimulationsHistory,
UserInventories,
UserCourses,
CourseMedia,
ExerciseQuestions,
UserLastLessons,
UserGlobalLastLessons,
UserCompletedManualSimulations,
QuestionCourseLabels,
QuestionSubjectLabels,
QuestionTopicLabels,
],
typescript: {
outputFile: path.resolve(__dirname, 'payload-types.ts'),
},
graphQL: {
schemaOutputFile: path.resolve(__dirname, 'generated-schema.graphql'),
},
// cors: ['http://localhost:3001', 'http://10.119.143.222:3001', '127.0.0.1:3001'],
// // cors: '*',
debug: true,
})
The above code is inside payload.config
useEffect(() => {
async function abc() {
const form = new FormData()
form.append('unset', 'true')
const options: RequestInit = {
method: 'GET',
// body: form,
credentials: 'include',
}
await fetch(`/backend/api/v1/pleasework`, options)
}
abc()
}, [])
The code above is inside nextjs to make the request when I visit a page
I confirm that now I can see the
user
object inside
req
! But this is a GET request, I will try really quick with a POST one to see if I can still see the user
In the POST request the user object is not there anymore
weird
It
shouldwork, its baffling
How would GET work and not POST
seems dubious
Anything in the network tab for the post?
I have made 2 separate endpoint for get and post to see exactly what's the difference
Both are delivered successfully, they differ in some headers, like the content-type in the POST request which is not needed in the GET
In the Request, the GET is empty, the POST has this
-----------------------------40592849741449068010483352937
Content-Disposition: form-data; name="unset"
true
-----------------------------40592849741449068010483352937--
hmm your credentials are not included π€
But as far as the browser goes, I'm not able to see the
user
object attached to the request, I can only see it in the
req
that is console logging in the backend terminal
Should they? I'm using the empty post request that you supplied earlier
are you passing credentials: 'include'
Ahh, yes
useEffect(() => {
async function abcget() {
const form = new FormData()
form.append('unset', 'true')
const options: RequestInit = {
method: 'GET',
// body: form,
credentials: 'include',
}
await fetch(`/backend/api/v1/pleaseworkget`, options)
}
abcget()
async function abcpost() {
const form = new FormData()
form.append('unset', 'true')
const options: RequestInit = {
method: 'POST',
body: form,
credentials: 'include',
}
await fetch(`/backend/api/v1/pleaseworkpost`, options)
}
abcpost()
}, [])
and its included for get and not for post?
Yes, wait I'll make a pastebin or similar so I can show you the console log, it's too long for discord
Never seen a bug like this, wonder what's causing it
the password for it is Z39gcUJgp5
I have separated the get and post request with a long
===========================
And each begin with
this is the post request
and
this is the fet request
Hmmm
The JWT on the response has the following data
{
"email": "dev@email.com",
"id": "63e1fae9af38ad9ec568f1e1",
"collection": "users",
"iat": 1686685431,
"exp": 1758685431
}
Yes, that's the get request
The get requests has the
user
, but the post request does not
POST JWT: {
"email": "dev@email.com",
"id": "63e1fae9af38ad9ec568f1e1",
"collection": "users",
"iat": 1686685431,
"exp": 1758685431
}
looks the same?
If get works, then post should work too...this is the bit that doesnt make sense to me
wait, what line is that?
Go to both requests
grab the payload-token
then go to
https://jwt.iopaste the token
both requests have the same data in the token
or responses rather
Oh, so you decoded the token?
well anyone could, but it's not "untampered" unless it validates with the secret token
so you know that it originates from a specific source
But the point is
Both requests/responses have the user in question in the JWT
which makes me think that both work, something is going on with the code
Paul may be more helpful at this point tho, I gotta drive home
:*(
Allright, thank you a lot for the help β€οΈ
drive safe
Thank you! Hopefully you can figure this out!
im out of ideas, checking in with chat gpt 4 on this one
hold on
Are we sure that the
user
object should be attached to the
req
object in a POST request?
to your post can you add a headers?
headers: { 'Content-Type': 'application/json' },
I'm betting my bugattis on it
So if the headers doesnt do it, can you inspect your cookie for me in the browser and lmk what config it has? you should see a
payload-token
cookie with some attributes like
sameSite
httpOnly
etc.
in your payload config I noticed youre missing
cors
and
csrf
config, these may be related π¬
set them both to an array of localhost:3000 and 3001
Just tried the headers, doesn't work
Actually I had some fight with that when I started coding the project. Basically to access my backend i'm using a NextJS rewrite, that goes like this
rewrites: async () => {
return [
{
source: '/backend/:path*',
destination: `${process.env.BACKEND}/:path*`
}
]
},
So it makes the code think that everything is on
localhost:3000
(the frontend)
otherwise my login wouldn't work at all
But I can try to restore the cors and csrf
OH.
Is it an "oh" in a bad way?
It's possible it's related yeah π€
(btw im looking a second through payload docs to see how to add cors and csrf)
Ohw, I thought that if
/me
worked then the config was good
should both be like
cors: [],
and
csrf: []
arrays of strings
I just wanna make sure, nextjs has been less than stellar recently for me
numerous bugs
Ok I did this
csrf: ['http://localhost:3001', 'http://localhost:3000'],
cors: ['http://localhost:3001', 'http://localhost:3000'],
I'll test it now
Still only works with GET
Can you try posting straight to payload, skipping next?
like, login first so you get the cookie set
and then do your endpoint
all from straight to payload...this is easier with postman cause you can more quickly make these requests
Okay, I'll try, just a thing, is it enough if I copy to postman the cookie I get from signin in directly into payload?
I use it to test my api but I'm not really good with it
I use insomnia, so idk the UI exactly but you can make it so it captures the token
it captures the cookie just like a browser would
and it can send them on other requests
Ohh I get it
but if the browser is easier for you then do that
i just wanna eliminate the rewrite being a problem
So I do a post request to /login with the credentials so it saves the cookies
(from postman)
you can do it from your frontend, just skip nextjs
as in the rewrite, so do a fetch to
payload/api
wherever that sits
I did the request straight from postman to
/v1/pleaseworkpost
and the
user
object showed in the request
ok its not payload's fault! >:)
I can't thank you enough for this, I don't know if I would have been able to troubleshoot by myself
it's possible your cookie being POSTed through a rewrite is either lost there or discarded by payload's middleware if the origin isnt quite right
chat gpt 4 helped us..confirmed that there are different situations where a GET and a POST with credentials will behave differently
π
Yo coming back to this thread, nice work guys!
Star
Discord
online
Get dedicated engineering support directly from the Payload team.