Skip to content

Commit 9f0c101

Browse files
authored
fix: getFieldsToSign crashes when user missing group/tab fields (#15775)
### What Fix crash in JWT generation when user documents are missing group or tab fields ### Why When a user document was created before group/tab fields were added to the schema, logging in would crash with "Cannot read properties of undefined" because `getFieldsToSign` tried to traverse into undefined fields. ### How Added nullish coalescing (`?? {}`) to default missing group/tab data to empty objects, consistent with how `afterRead/promise.ts` handles this case. Fixes #15734
1 parent fd81fd5 commit 9f0c101

File tree

2 files changed

+42
-6
lines changed

2 files changed

+42
-6
lines changed

packages/payload/src/auth/getFieldsToSign.ts

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,16 @@ const traverseFields = ({
2828
}
2929
case 'group': {
3030
if (fieldAffectsData(field)) {
31+
const groupData: Record<string, unknown> =
32+
(data[field.name] as Record<string, unknown>) ?? {}
3133
let targetResult
3234
if (typeof field.saveToJWT === 'string') {
3335
targetResult = field.saveToJWT
34-
result[field.saveToJWT] = data[field.name]
36+
result[field.saveToJWT] = groupData
3537
} else if (field.saveToJWT) {
3638
targetResult = field.name
37-
result[field.name] = data[field.name]
39+
result[field.name] = groupData
3840
}
39-
const groupData: Record<string, unknown> = data[field.name] as Record<string, unknown>
4041
const groupResult = (targetResult ? result[targetResult] : result) as Record<
4142
string,
4243
unknown
@@ -59,15 +60,16 @@ const traverseFields = ({
5960
}
6061
case 'tab': {
6162
if (tabHasName(field)) {
63+
const tabData: Record<string, unknown> =
64+
(data[field.name] as Record<string, unknown>) ?? {}
6265
let targetResult
6366
if (typeof field.saveToJWT === 'string') {
6467
targetResult = field.saveToJWT
65-
result[field.saveToJWT] = data[field.name]
68+
result[field.saveToJWT] = tabData
6669
} else if (field.saveToJWT) {
6770
targetResult = field.name
68-
result[field.name] = data[field.name]
71+
result[field.name] = tabData
6972
}
70-
const tabData: Record<string, unknown> = data[field.name] as Record<string, unknown>
7173
const tabResult = (targetResult ? result[targetResult] : result) as Record<
7274
string,
7375
unknown

test/auth/int.spec.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type {
1010
import crypto from 'crypto'
1111
import { jwtDecode } from 'jwt-decode'
1212
import path from 'path'
13+
import { getFieldsToSign } from 'payload'
1314
import { email as emailValidation } from 'payload/shared'
1415
import { fileURLToPath } from 'url'
1516
import { v4 as uuid } from 'uuid'
@@ -270,6 +271,39 @@ describe('Auth', () => {
270271
expect(exp).toBeDefined()
271272
})
272273

274+
it('should not crash when building JWT for user with missing group/tab fields', () => {
275+
const collectionConfig = payload.collections[slug].config
276+
277+
// Simulate a user document that was created before group/tab fields were added.
278+
// Without the fix, getFieldsToSign would crash with:
279+
// "TypeError: Cannot read properties of undefined (reading 'saveToJWTString')"
280+
// when trying to traverse into groupSaveToJWT.saveToJWTString
281+
const userWithMissingFields = {
282+
id: '123',
283+
email: 'test@example.com',
284+
roles: ['user'],
285+
// Missing fields: group, groupSaveToJWT, saveToJWTTab, tabSaveToJWTString
286+
}
287+
288+
expect(() => {
289+
getFieldsToSign({
290+
collectionConfig,
291+
email: userWithMissingFields.email,
292+
user: userWithMissingFields as any,
293+
})
294+
}).not.toThrow()
295+
296+
const result = getFieldsToSign({
297+
collectionConfig,
298+
email: userWithMissingFields.email,
299+
user: userWithMissingFields as any,
300+
})
301+
302+
expect(result.id).toBe(userWithMissingFields.id)
303+
expect(result.email).toBe(userWithMissingFields.email)
304+
expect(result.collection).toBe(slug)
305+
})
306+
273307
it('should allow authentication with an API key with useAPIKey', async () => {
274308
const apiKey = '0123456789ABCDEFGH'
275309

0 commit comments

Comments
 (0)