diff --git a/docs/authentication/overview.mdx b/docs/authentication/overview.mdx
index 0bf7b6a92f..32bedf00ce 100644
--- a/docs/authentication/overview.mdx
+++ b/docs/authentication/overview.mdx
@@ -83,9 +83,11 @@ Once enabled, each document that is created within the Collection can be thought
Successfully logging in returns a `JWT` (JSON web token) which is how a user will identify themselves to Payload. By providing this JWT via either an HTTP-only cookie or an `Authorization` header, Payload will automatically identify the user and add its user JWT data to the Express `req`, which is available throughout Payload including within access control, hooks, and more.
+You can specify what data gets encoded to the JWT token by setting `saveToJWT` to true in your auth collection fields. If you wish to use a different key other than the field `name`, you can provide it to `saveToJWT` as a string.
+
Tip:
- You can access the logged in user from access control functions and hooks via the Express req. The logged in user is automatically added as the user property.
+ You can access the logged-in user from access control functions and hooks via the Express req. The logged-in user is automatically added as the user property.
### HTTP-only cookies
diff --git a/src/auth/operations/getFieldsToSign.ts b/src/auth/operations/getFieldsToSign.ts
index e5a86cf9e1..83ad020bd0 100644
--- a/src/auth/operations/getFieldsToSign.ts
+++ b/src/auth/operations/getFieldsToSign.ts
@@ -18,16 +18,17 @@ export const getFieldsToSign = (args: {
...signedFields,
};
+ // get subfields from non-named fields like rows
if (!fieldAffectsData(field) && fieldHasSubFields(field)) {
field.fields.forEach((subField) => {
if (fieldAffectsData(subField) && subField.saveToJWT) {
- result[subField.name] = user[subField.name];
+ result[typeof subField.saveToJWT === 'string' ? subField.saveToJWT : subField.name] = user[subField.name];
}
});
}
if (fieldAffectsData(field) && field.saveToJWT) {
- result[field.name] = user[field.name];
+ result[typeof field.saveToJWT === 'string' ? field.saveToJWT : field.name] = user[field.name];
}
return result;
diff --git a/src/auth/operations/resetPassword.ts b/src/auth/operations/resetPassword.ts
index c4e04830f8..20635c2943 100644
--- a/src/auth/operations/resetPassword.ts
+++ b/src/auth/operations/resetPassword.ts
@@ -3,7 +3,7 @@ import { Response } from 'express';
import { Collection } from '../../collections/config/types';
import { APIError } from '../../errors';
import getCookieExpiration from '../../utilities/getCookieExpiration';
-import { fieldAffectsData } from '../../fields/config/types';
+import { getFieldsToSign } from './getFieldsToSign';
import { PayloadRequest } from '../../express/types';
import { authenticateLocalStrategy } from '../strategies/local/authenticate';
import { generatePasswordSaltHash } from '../strategies/local/generatePasswordSaltHash';
@@ -83,18 +83,10 @@ async function resetPassword(args: Arguments): Promise {
await authenticateLocalStrategy({ password: data.password, doc });
- const fieldsToSign = collectionConfig.fields.reduce((signedFields, field) => {
- if (fieldAffectsData(field) && field.saveToJWT) {
- return {
- ...signedFields,
- [field.name]: user[field.name],
- };
- }
- return signedFields;
- }, {
+ const fieldsToSign = getFieldsToSign({
+ collectionConfig,
+ user,
email: user.email,
- id: user.id,
- collection: collectionConfig.slug,
});
const token = jwt.sign(
diff --git a/src/fields/config/schema.ts b/src/fields/config/schema.ts
index 2d1f2dd732..c1125aa6f9 100644
--- a/src/fields/config/schema.ts
+++ b/src/fields/config/schema.ts
@@ -33,7 +33,10 @@ export const baseField = joi.object().keys({
joi.valid(false),
),
required: joi.boolean().default(false),
- saveToJWT: joi.boolean().default(false),
+ saveToJWT: joi.alternatives().try(
+ joi.boolean(),
+ joi.string(),
+ ).default(false),
unique: joi.boolean().default(false),
localized: joi.boolean().default(false),
index: joi.boolean().default(false),
diff --git a/src/fields/config/types.ts b/src/fields/config/types.ts
index 6b0bdc234c..edbc0b6e8b 100644
--- a/src/fields/config/types.ts
+++ b/src/fields/config/types.ts
@@ -1,7 +1,7 @@
/* eslint-disable no-use-before-define */
import { CSSProperties } from 'react';
import { Editor } from 'slate';
-import type { TFunction, i18n as Ii18n } from 'i18next';
+import type { i18n as Ii18n, TFunction } from 'i18next';
import type { EditorProps } from '@monaco-editor/react';
import { Operation, Where } from '../../types';
import { SanitizedConfig } from '../../config/types';
@@ -108,7 +108,7 @@ export interface FieldBase {
index?: boolean;
defaultValue?: any;
hidden?: boolean;
- saveToJWT?: boolean
+ saveToJWT?: string | boolean;
localized?: boolean;
validate?: Validate;
hooks?: {
diff --git a/test/auth/config.ts b/test/auth/config.ts
index 0e396976db..d406618392 100644
--- a/test/auth/config.ts
+++ b/test/auth/config.ts
@@ -6,6 +6,10 @@ import { AuthDebug } from './AuthDebug';
export const slug = 'users';
+export const namedSaveToJWTValue = 'namedSaveToJWT value';
+
+export const saveToJWTKey = 'x-custom-jwt-property-name';
+
export default buildConfigWithDefaults({
admin: {
user: 'users',
@@ -38,6 +42,12 @@ export default buildConfigWithDefaults({
saveToJWT: true,
hasMany: true,
},
+ {
+ name: 'namedSaveToJWT',
+ type: 'text',
+ defaultValue: namedSaveToJWTValue,
+ saveToJWT: saveToJWTKey,
+ },
{
name: 'custom',
label: 'Custom',
diff --git a/test/auth/int.spec.ts b/test/auth/int.spec.ts
index 6d5cd1547f..af866e4d61 100644
--- a/test/auth/int.spec.ts
+++ b/test/auth/int.spec.ts
@@ -1,7 +1,8 @@
import mongoose from 'mongoose';
+import jwtDecode from 'jwt-decode';
import payload from '../../src';
import { initPayloadTest } from '../helpers/configHelpers';
-import { slug } from './config';
+import { namedSaveToJWTValue, saveToJWTKey, slug } from './config';
import { devUser } from '../credentials';
import type { User } from '../../src/auth';
@@ -101,6 +102,24 @@ describe('Auth', () => {
expect(data.user.email).toBeDefined();
});
+ it('should have fields saved to JWT', async () => {
+ const {
+ email: jwtEmail,
+ collection,
+ roles,
+ [saveToJWTKey]: customJWTPropertyKey,
+ iat,
+ exp,
+ } = jwtDecode(token);
+
+ expect(jwtEmail).toBeDefined();
+ expect(collection).toEqual('users');
+ expect(Array.isArray(roles)).toBeTruthy();
+ // 'x-custom-jwt-property-name': 'namedSaveToJWT value'
+ expect(customJWTPropertyKey).toEqual(namedSaveToJWTValue);
+ expect(iat).toBeDefined();
+ expect(exp).toBeDefined();
+ });
it('should allow authentication with an API key with useAPIKey', async () => {
const apiKey = '0123456789ABCDEFGH';