Skip to content

Commit 6104fe5

Browse files
authored
feat: disableLocalStrategy with auth fields still enabled (#9579)
Adds configuration options to `auth.disableLocalStrategy` to allow customization of how payload treats an auth enabled collection. Two new properties have been added to `disableLocalStrategy`: - `enableFields` Include auth fields on the collection even though the local strategy is disabled. Useful when you do not want the database or types to vary depending on the auth configuration used. - `optionalPassword`: makes the password field not required
1 parent 40f5c72 commit 6104fe5

File tree

16 files changed

+256
-41
lines changed

16 files changed

+256
-41
lines changed

packages/graphql/src/schema/initCollections.ts

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,12 +126,20 @@ export function initCollections({ config, graphqlResult }: InitCollectionsGraphQ
126126

127127
const mutationInputFields = [...fields]
128128

129-
if (collectionConfig.auth && !collectionConfig.auth.disableLocalStrategy) {
129+
if (
130+
collectionConfig.auth &&
131+
(!collectionConfig.auth.disableLocalStrategy ||
132+
(typeof collectionConfig.auth.disableLocalStrategy === 'object' &&
133+
collectionConfig.auth.disableLocalStrategy.optionalPassword))
134+
) {
130135
mutationInputFields.push({
131136
name: 'password',
132137
type: 'text',
133138
label: 'Password',
134-
required: true,
139+
required: !(
140+
typeof collectionConfig.auth.disableLocalStrategy === 'object' &&
141+
collectionConfig.auth.disableLocalStrategy.optionalPassword
142+
),
135143
})
136144
}
137145

packages/payload/src/auth/getAuthFields.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,11 @@ export const getBaseAuthFields = (authConfig: IncomingAuthType): Field[] => {
1515
authFields.push(...apiKeyFields)
1616
}
1717

18-
if (!authConfig.disableLocalStrategy) {
18+
if (
19+
!authConfig.disableLocalStrategy ||
20+
(typeof authConfig.disableLocalStrategy === 'object' &&
21+
authConfig.disableLocalStrategy.enableFields)
22+
) {
1923
const emailField = { ...emailFieldConfig }
2024
let usernameField: TextField | undefined
2125

packages/payload/src/auth/operations/forgotPassword.ts

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import type { PayloadRequest, Where } from '../../types/index.js'
1111

1212
import { buildAfterOperation } from '../../collections/operations/utils.js'
1313
import { APIError } from '../../errors/index.js'
14+
import { Forbidden } from '../../index.js'
1415
import { commitTransaction } from '../../utilities/commitTransaction.js'
1516
import { initTransaction } from '../../utilities/initTransaction.js'
1617
import { killTransaction } from '../../utilities/killTransaction.js'
@@ -43,15 +44,18 @@ export const forgotPasswordOperation = async <TSlug extends CollectionSlug>(
4344
? data.username.toLowerCase().trim()
4445
: null
4546

47+
let args = incomingArgs
48+
49+
if (incomingArgs.collection.config.auth.disableLocalStrategy) {
50+
throw new Forbidden(incomingArgs.req.t)
51+
}
4652
if (!sanitizedEmail && !sanitizedUsername) {
4753
throw new APIError(
4854
`Missing ${loginWithUsername ? 'username' : 'email'}.`,
4955
httpStatus.BAD_REQUEST,
5056
)
5157
}
5258

53-
let args = incomingArgs
54-
5559
try {
5660
const shouldCommit = await initTransaction(args.req)
5761

@@ -74,7 +78,6 @@ export const forgotPasswordOperation = async <TSlug extends CollectionSlug>(
7478

7579
const {
7680
collection: { config: collectionConfig },
77-
data,
7881
disableEmail,
7982
expiration,
8083
req: {

packages/payload/src/auth/operations/login.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import type { User } from '../types.js'
1010
import { buildAfterOperation } from '../../collections/operations/utils.js'
1111
import { AuthenticationError, LockedAuth, ValidationError } from '../../errors/index.js'
1212
import { afterRead } from '../../fields/hooks/afterRead/index.js'
13+
import { Forbidden } from '../../index.js'
1314
import { killTransaction } from '../../utilities/killTransaction.js'
1415
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields.js'
1516
import { getFieldsToSign } from '../getFieldsToSign.js'
@@ -40,6 +41,10 @@ export const loginOperation = async <TSlug extends CollectionSlug>(
4041
): Promise<{ user: DataFromCollectionSlug<TSlug> } & Result> => {
4142
let args = incomingArgs
4243

44+
if (args.collection.config.auth.disableLocalStrategy) {
45+
throw new Forbidden(args.req.t)
46+
}
47+
4348
try {
4449
// /////////////////////////////////////
4550
// beforeOperation - Collection

packages/payload/src/auth/operations/registerFirstUser.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,10 @@ export const registerFirstUserOperation = async <TSlug extends CollectionSlug>(
4242
req: { payload },
4343
} = args
4444

45+
if (config.auth.disableLocalStrategy) {
46+
throw new Forbidden(req.t)
47+
}
48+
4549
try {
4650
const shouldCommit = await initTransaction(req)
4751

packages/payload/src/auth/operations/resetPassword.ts

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import httpStatus from 'http-status'
33
import type { Collection } from '../../collections/config/types.js'
44
import type { PayloadRequest } from '../../types/index.js'
55

6-
import { APIError } from '../../errors/index.js'
6+
import { APIError, Forbidden } from '../../errors/index.js'
77
import { commitTransaction } from '../../utilities/commitTransaction.js'
88
import { initTransaction } from '../../utilities/initTransaction.js'
99
import { killTransaction } from '../../utilities/killTransaction.js'
@@ -29,13 +29,6 @@ export type Arguments = {
2929
}
3030

3131
export const resetPasswordOperation = async (args: Arguments): Promise<Result> => {
32-
if (
33-
!Object.prototype.hasOwnProperty.call(args.data, 'token') ||
34-
!Object.prototype.hasOwnProperty.call(args.data, 'password')
35-
) {
36-
throw new APIError('Missing required data.', httpStatus.BAD_REQUEST)
37-
}
38-
3932
const {
4033
collection: { config: collectionConfig },
4134
data,
@@ -48,6 +41,17 @@ export const resetPasswordOperation = async (args: Arguments): Promise<Result> =
4841
req,
4942
} = args
5043

44+
if (
45+
!Object.prototype.hasOwnProperty.call(data, 'token') ||
46+
!Object.prototype.hasOwnProperty.call(data, 'password')
47+
) {
48+
throw new APIError('Missing required data.', httpStatus.BAD_REQUEST)
49+
}
50+
51+
if (collectionConfig.auth.disableLocalStrategy) {
52+
throw new Forbidden(req.t)
53+
}
54+
5155
try {
5256
const shouldCommit = await initTransaction(req)
5357

packages/payload/src/auth/operations/unlock.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import type { CollectionSlug } from '../../index.js'
88
import type { PayloadRequest, Where } from '../../types/index.js'
99

1010
import { APIError } from '../../errors/index.js'
11+
import { Forbidden } from '../../index.js'
1112
import { commitTransaction } from '../../utilities/commitTransaction.js'
1213
import { initTransaction } from '../../utilities/initTransaction.js'
1314
import { killTransaction } from '../../utilities/killTransaction.js'
@@ -44,6 +45,9 @@ export const unlockOperation = async <TSlug extends CollectionSlug>(
4445
args.data.username.toLowerCase().trim()) ||
4546
null
4647

48+
if (collectionConfig.auth.disableLocalStrategy) {
49+
throw new Forbidden(req.t)
50+
}
4751
if (!sanitizedEmail && !sanitizedUsername) {
4852
throw new APIError(
4953
`Missing ${collectionConfig.auth.loginWithUsername ? 'username' : 'email'}.`,

packages/payload/src/auth/operations/verifyEmail.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import httpStatus from 'http-status'
33
import type { Collection } from '../../collections/config/types.js'
44
import type { PayloadRequest } from '../../types/index.js'
55

6-
import { APIError } from '../../errors/index.js'
6+
import { APIError, Forbidden } from '../../errors/index.js'
77
import { commitTransaction } from '../../utilities/commitTransaction.js'
88
import { initTransaction } from '../../utilities/initTransaction.js'
99
import { killTransaction } from '../../utilities/killTransaction.js'
@@ -16,6 +16,10 @@ export type Args = {
1616

1717
export const verifyEmailOperation = async (args: Args): Promise<boolean> => {
1818
const { collection, req, token } = args
19+
20+
if (collection.config.auth.disableLocalStrategy) {
21+
throw new Forbidden(req.t)
22+
}
1923
if (!Object.prototype.hasOwnProperty.call(args, 'token')) {
2024
throw new APIError('Missing required data.', httpStatus.BAD_REQUEST)
2125
}

packages/payload/src/auth/types.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,16 @@ export interface IncomingAuthType {
206206
/**
207207
* Advanced - disable Payload's built-in local auth strategy. Only use this property if you have replaced Payload's auth mechanisms with your own.
208208
*/
209-
disableLocalStrategy?: true
209+
disableLocalStrategy?:
210+
| {
211+
/**
212+
* Include auth fields on the collection even though the local strategy is disabled.
213+
* Useful when you do not want the database or types to vary depending on the auth configuration.
214+
*/
215+
enableFields?: true
216+
optionalPassword?: true
217+
}
218+
| true
210219
/**
211220
* Customize the way that the forgotPassword operation functions.
212221
* @link https://payloadcms.com/docs/authentication/email#forgot-password

packages/payload/src/utilities/configToJSONSchema.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -617,7 +617,13 @@ export function entityToJSONSchema(
617617
})
618618
}
619619

620-
if ('auth' in entity && entity.auth && !entity.auth?.disableLocalStrategy) {
620+
if (
621+
'auth' in entity &&
622+
entity.auth &&
623+
(!entity.auth?.disableLocalStrategy ||
624+
(typeof entity.auth?.disableLocalStrategy === 'object' &&
625+
entity.auth.disableLocalStrategy.enableFields))
626+
) {
621627
entity.flattenedFields.push({
622628
name: 'password',
623629
type: 'text',

0 commit comments

Comments
 (0)