#Login SUCCESS, /api/users/me request failing

1 messages · Page 1 of 1 (latest)

tough orbit
#

Similar but different to thread: https://discord.com/channels/967097582721572934/1179404220106035291

Setup:

  • I'm running Payload@beta with a Next.js app in my local dev environment (Mac, VScode, etc.)
  • My login page is /signin which calls /api/users/login in handleSubmit()
  • Login successfully completes and creates a valid payload-token cookie which I have verified is correct here: https://jwt.io/
  • I have a React HOC withAuth() wrapping the page components in my app
  • Inside withAuth() I am calling /api/users/me to verify the user is logged in so I can restrict access based on roles

ERROR

  • /api/users/me is returning a 200 response, but the response.json() data is { user: null, message: 'Account' }

Troubleshooting

  • I have confirmed process.env.SERVER_URL is set correctly as http://localhost:3000/
  • I have cors and csrf configured in payload.config.ts, and set to the correct .env variable
  • I have tried the /api/users?where[id][equals]=${encodeURIComponent('me')} endpoint which returns the following
{
  docs: [],
  totalDocs: 0,
  limit: 10,
  totalPages: 1,
  page: 1,
  pagingCounter: 1,
  hasPrevPage: false,
  hasNextPage: false,
  prevPage: null,
  nextPage: null
}

Any ideas where to go next with this?

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

tough orbit
#

Login SUCCESS, /api/users/me request failing

pure hawk
#

can you send your access configuration or hooks?

tough orbit
#

Hi minisomai, the access config for my user collection is: access: { // Only admins can create users create: isAnonymous, // Admins can read all, but any other logged in user can only read themselves read: isAnonymous, // Admins can update all, but any other logged in user can only update themselves update: isAdminOrSelf, // Only admins can delete delete: isAdmin, },

#

I changed create to isAnonymous but didn't update the comment. (clearly)

tough orbit
#

This may be unrelated, but I just tried a test to decode the JWT token from a new server-side endpoint at /api/cookies. ```import { cookies } from 'next/headers'
import { NextResponse } from 'next/server'
import jwt from 'jsonwebtoken'

export async function GET() {
const cookieStore = cookies()
const token = cookieStore.get('payload-token')
console.log('token', token)
if (!token) {
return NextResponse.json({ error: 'No token found' }, { status: 401 })
}

try {
const secret = process.env.PAYLOAD_SECRET
if (!secret) {
throw new Error('PAYLOAD_SECRET is not set')
}
console.log('secret', secret)

const decoded = jwt.verify(token.value, process.env.PAYLOAD_SECRET as string) as { id: string }
console.log('decoded', decoded)

return NextResponse.json({ userId: decoded.id })

} catch (error) {
console.log('error', error)
return NextResponse.json({ error: 'Invalid token' }, { status: 401 })
}
}

#

Here is the console output I get from hitting: http://localhost:3000/api/cookies ```token {
name: 'payload-token',
value: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY2ZDVkOGM5ZWM0MTQzZmYzMjk5Njc2MiIsImNvbGxlY3Rpb24iOiJ1c2VycyIsImVtYWlsIjoiamFtZXMyQHNtb3Zlci5jbyIsImFjdGl2ZSI6dHJ1ZSwicm9sZXMiOlsiZWRpdG9yIl0sImlhdCI6MTcyNTYxOTU0MSwiZXhwIjoxNzI4MjExNTQxfQ.57627rxl-wnMGzYo5wR_7_eavjhiR3exkUzVfR2Ms74',
path: '/'
}
secret jawliejfilwajefSEANlawefawfewag349jwgo3gj4w
error JsonWebTokenError: invalid signature
at eval (webpack-internal:///(rsc)/./node_modules/jsonwebtoken/verify.js:171:19)
at getSecret (webpack-internal:///(rsc)/./node_modules/jsonwebtoken/verify.js:97:14)
at module.exports [as verify] (webpack-internal:///(rsc)/./node_modules/jsonwebtoken/verify.js:101:10)
at GET (webpack-internal:///(rsc)/./src/app/(payload)/api/cookies/route.ts:29:77)
...

(This is just a dev secret, but yes I'll be rotating it after posting here. 🤓 )
#

When I took a closer look at https://jwt.io/ for the value of my payload-token cookie, I'm able to see the unpacked contents of the token, but I'm also seeing an "Invalid Signature" error there until I add my PAYLOAD_SECRET to the VERIFY SIGNATURE field on the webpage.

JSON Web Tokens are an open, industry standard RFC 7519 method for representing claims securely between two parties.

#

So clearly the token being generated by the /api/users/login endpoint and stored in the payload-token cookie is correct for my process.env.PAYLOAD_SECRET setting. But something isn't configured right for /api/users/me to get access to that credential, or even for jwt.verify() to decode correctly.

knotty quiver
#

Is it the hash maybe?

#

I remember doing something like this in v2, not sure if it's related

#
import crypto from 'crypto'

const verifyToken = (req: express.Request, res: express.Response): boolean => {
  if (req.cookies["payload-token"]) {
    const hash = crypto.createHash('sha256').update(process.env.PAYLOAD_SECRET).digest('hex').slice(32)

    try {
      const decoded = jwt.verify(req.cookies["payload-token"], hash, {
        algorithms: ["HS256"],
      });
      console.log(decoded)
      return true;
    } catch (err) {
      console.log("Invalid token request.", err);
      return false;
    }
  } else {
    console.log("Missing token in request.");
    return false;
  }
};
#

@tough orbit

tough orbit
#

Here, thanks for the ping.

#

oooohhh!!! That could be it. I'll give that a try and see.

#

That would fix my manual cookie decode issue, but doesn't explain Payload /api/users/me not working correctly.

knotty quiver
#

Hmm

#

Maybe the 200 is technically correct since it wasn't a failed response

#

It was just, not a response with the expected data

#

Like, if I were to hit that endpoint to see if I have an active user session, I'd want a 200 back

#

and I'd check the user property

tough orbit
#

FWIW, /api/logout isn't working either. It's not removing the payload-token cookie.

knotty quiver
#

Are you sending the request with credentials: include ?

tough orbit
#

Yes, all of these requests are being sent with credentials: include: /api/login, /api/users/me, /api/logout

knotty quiver
#

And logout succeeds?

#

(the request)

tough orbit
#

It's infuriatingly strange. 💢

knotty quiver
#

We could try a few things out

tough orbit
#

It's the built-in so I can't/haven't instrumented it. But /api/logout runs with a 200 in the console, then redirects to /login

#

I would appreciate the help.

knotty quiver
#

The only times I've seen inconsistent cookie things like this is when there is an issue with the serverURL

#

Which doesn't seem to be the case

#

Are you testing this locally or on an actually https:// domain

tough orbit
#

This is just local for right now. We're deploying in Docker on GoogleCloudBuild/Run

knotty quiver
#

Well just for "yucks", can you try removing the serverURL

#

I don't think it will fix it, but I saw another post where someone claimed that helped on local

#

Not sure how tbh, seems like it would break things

#

But worth a shot?

tough orbit
#

Hahah, I saw that too. I think I tried it at 2am, but went to bed after and don't remember.

#

Ok, so I just retried /signin which calls /api/login I have configured to then check /api/users/me to retrieve user details. Here's what I see in server console: ``` GET /signin 200 in 78ms
POST /api/users/login 200 in 169ms
response Response {
status: 200,
statusText: '',
headers: Headers {
'access-control-allow-headers': 'Origin, X-Requested-With, Content-Type, Accept, Authorization, Content-Encoding, x-apollo-tracing',
'access-control-allow-methods': 'PUT, PATCH, POST, GET, DELETE, OPTIONS',
connection: 'keep-alive',
'content-type': 'application/json',
date: 'Fri, 06 Sep 2024 13:10:07 GMT',
'keep-alive': 'timeout=5',
'transfer-encoding': 'chunked',
vary: 'RSC, Next-Router-State-Tree, Next-Router-Prefetch'
},
body: ReadableStream { locked: false, state: 'readable', supportsBYOB: true },
bodyUsed: false,
ok: true,
redirected: false,
type: 'default',
url: 'http://localhost:3000/api/users/me'
}
data { user: null, message: 'Account' }

#

And the cookie is created.

#

loading /logout gives me this: GET /logout 307 in 2803ms

#

And the payload-token cookie has not been deleted.

knotty quiver
#

I think logout needs to be a POST

#

So that it can remove the token

tough orbit
#

Hmmm ... ok. Let's come back to that.

knotty quiver
#

Weird that you dont get the user back

#

I dont see the credential include in the request

tough orbit
#

Curiously, if I change from /api/users/me to /api/users?where[id][equals]=${encodeURIComponent('me')} I get the following: ```GET /signin 200 in 260ms
○ Compiling /api/[...slug] ...
✓ Compiled /api/[...slug] in 3s (5052 modules)
POST /api/users/login 200 in 4206ms
response Response {
status: 200,
statusText: '',
headers: Headers {
'access-control-allow-headers': 'Origin, X-Requested-With, Content-Type, Accept, Authorization, Content-Encoding, x-apollo-tracing',
'access-control-allow-methods': 'PUT, PATCH, POST, GET, DELETE, OPTIONS',
connection: 'keep-alive',
'content-type': 'application/json',
date: 'Fri, 06 Sep 2024 13:09:18 GMT',
'keep-alive': 'timeout=5',
'transfer-encoding': 'chunked',
vary: 'RSC, Next-Router-State-Tree, Next-Router-Prefetch'
},
body: ReadableStream { locked: false, state: 'readable', supportsBYOB: true },
bodyUsed: false,
ok: true,
redirected: false,
type: 'default',
url: 'http://localhost:3000/api/users?where[id][equals]=me'
}
data {
docs: [],
totalDocs: 0,
limit: 10,
totalPages: 1,
page: 1,
pagingCounter: 1,
hasPrevPage: false,
hasNextPage: false,
prevPage: null,
nextPage: null
}

knotty quiver
#

The login request should have the user on the response

#

(Though, users/me should too - unless credentials dont get passed in)

tough orbit
#

Here's the code I'm running for reference: ```export const authorizeUser = async () => {
try {
const response = await fetch(
${process.env.NEXT_PUBLIC_BASE_URL}/api/users?where[id][equals]=${encodeURIComponent('me')},
{
method: 'GET',
credentials: 'include',
headers: {
'Content-Type': 'application/json',
},
},
)
if (!response.ok) {
throw new Error('Failed to fetch user data')
}
console.log('response', response)
const data = await response.json()
console.log('data', data)
return data.docs[0]
} catch (error) {
console.error('Error in authorizeUser:', error)
return null
}
}

#

process.env.NEXT_PUBLIC_BASE_URL is set to http://localhost:3000 in my .env.local file.

knotty quiver
#

Hmmm

#

And you've set CORS / CSRF?

tough orbit
#

Here's what's in my config: ```...
secret: process.env.PAYLOAD_SECRET || '',
...
serverURL: process.env.NEXT_PUBLIC_SERVER_URL || '',
cors: [${process.env.NEXT_PUBLIC_BASE_URL}],
csrf: [${process.env.NEXT_PUBLIC_BASE_URL}],
...

knotty quiver
#

In the response, do you get any messages back?

#

In your dev tools?

#

Also, can you try to replace the env vars with their hardcoded values, for now

tough orbit
# knotty quiver In your dev tools?

Here's what I have in the browser console: sign in response {message: 'Authentication Passed', exp: 1725635935, token: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY2Z…TM1fQ.VSoPJUtEBoyDuB2-JpwRd4sXos2IvRxvFpRiazFko0U', user: {…}}exp: 1725635935message: "Authentication Passed"token: "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpZCI6IjY2ZDVkOGM5ZWM0MTQzZmYzMjk5Njc2MiIsImNvbGxlY3Rpb24iOiJ1c2VycyIsImVtYWlsIjoiamFtZXMyQHNtb3Zlci5jbyIsImFjdGl2ZSI6dHJ1ZSwicm9sZXMiOlsiZWRpdG9yIl0sImlhdCI6MTcyNTYyODczNSwiZXhwIjoxNzI1NjM1OTM1fQ.VSoPJUtEBoyDuB2-JpwRd4sXos2IvRxvFpRiazFko0U"user: active: truecreatedAt: "2024-09-02T15:24:57.977Z"email: "[email protected]"id: "66d5d8c9ec4143ff32996762"loginAttempts: 0recieveEmail: truerecieveText: trueroles: ['editor']socialMedia: {}updatedAt: "2024-09-02T15:24:57.977Z"[[Prototype]]: Object[[Prototype]]: Object app-index.js:34 Error: Cannot read properties of undefined (reading 'id')

knotty quiver
#

The user info is in there

tough orbit
#

Right!

knotty quiver
#

Whats with the undefined?

tough orbit
#

That's the code on the page trying to reference the user object, but not finding it.

knotty quiver
#

Hmm

#

I need to switch to another issue for a tiny bit, but I shall return to help troubleshoot

tough orbit
#

Thanks @knotty quiver, I'm in a meeting right now so I'll take a break too.

knotty quiver
#

No prob!

tough orbit
#

TEST: I'm going to try and remove serverURL from my Payload config.

#

RESULT: No change in bahavior with serverURL disabled.

#

TEST: I'm going to try hard coding process.env references.

#

RESULT: No change in behavior hard coding payload.config.ts settings.

tough orbit
# tough orbit Here's what I have in the browser console: ```sign in response {message: 'Authen...

So the 'sign in response' console.log line is coming from /signin and is the reponse from /api/users/login. The code then redirects to /dashboard which tries to limit access to logged in users by calling the authorizeUser() function I referenced above. That is the function expected to return a user object from /api/users/me and since it's NULL, the rest of the code on /dashboard is failing. (I've disabled the correct behavior on /dashboard so I can capture these errors and debug)

knotty quiver
#

Hmmm

#

Hey I'm back!

#

I'm looking at the logout logic for Payload, and I cant think of a reason the cookie wouldn't be removed

https://github.com/payloadcms/payload/blob/ba7a043a99f58fad39a62ac471eeb7309a39bba0/packages/payload/src/auth/operations/logout.ts#L17

GitHub

Payload is the open-source, fullstack Next.js framework, giving you instant backend superpowers. Get a full TypeScript backend and admin panel instantly. Use Payload as a headless CMS or for buildi...

tough orbit
#

Hey, thanks for checking in. I'm rebuilding a clean Next/Payload install on latest versions to see if it's still an issue.

marsh mauve
#

having the same issue. Anyone was able to fix it ?

#

I spent half a day on that issue and for me it turned out to be problem with config. I had following config set in payload config in auth section: verify: true, // Require email verification before being allowed to authenticate