fix: keep apiKey encrypted in refresh operation (#13063) (#13177)

### What?
Prevents decrypted apiKey from being saved back to database on the auth
refresh operation.

### Why?
References issue #13063: refreshing a token for a logged-in user
decrypted `apiKey` and wrote it back in plaintext, corrupting the user
record.

### How?
The user is now fetched with `db.findOne` instead of `findByID`,
preserving the encryption of the key when saved back to the database
using `db.updateOne`. The user record is then re-fetched using
`findByID`, allowing for the decrypted key to be provided in the
response.

### Tests
*  keeps apiKey encrypted in DB after refresh
*  returns user with decrypted apiKey after refresh

Fixes #13063
This commit is contained in:
contip
2025-07-29 16:27:45 -04:00
committed by GitHub
parent 08942494e3
commit b1fa76e397
2 changed files with 48 additions and 7 deletions

View File

@@ -262,6 +262,42 @@ describe('Auth', () => {
expect(data.user.custom).toBe('Goodbye, world!')
})
it('keeps apiKey encrypted in DB after refresh operation', async () => {
const apiKey = '987e6543-e21b-12d3-a456-426614174999'
const user = await payload.create({
collection: slug,
data: { email: 'user@example.com', password: 'Password123', apiKey, enableAPIKey: true },
})
const { token } = await payload.login({
collection: 'users',
data: { email: 'user@example.com', password: 'Password123' },
})
await restClient.POST('/users/refresh-token', {
headers: { Authorization: `JWT ${token}` },
})
const raw = await payload.db.findOne<any>({
collection: 'users',
req: { locale: 'en' } as any,
where: { id: { equals: user.id } },
})
expect(raw?.apiKey).not.toContain('-') // still ciphertext
})
it('returns a user with decrypted apiKey after refresh', async () => {
const { token } = await payload.login({
collection: 'users',
data: { email: 'user@example.com', password: 'Password123' },
})
const res = await restClient
.POST('/users/refresh-token', {
headers: { Authorization: `JWT ${token}` },
})
.then((r) => r.json())
expect(res.user.apiKey).toMatch(/[0-9a-f-]{36}/) // UUID string
})
it('should allow a user to be created', async () => {
const response = await restClient.POST(`/${slug}`, {
body: JSON.stringify({