fix: allows for emails to be non unique when allowEmailLogin is false (#9541)
### What? When you prevent users from authenticating with their email, we should not enforce uniqueness on the email field. ### Why? We never set the unique property to false. ### How? Set the unique property to false if `loginWithUsername.allowEmailLogin` is `false`.
This commit is contained in:
@@ -14,7 +14,7 @@ import {
|
||||
GraphQLString,
|
||||
} from 'graphql'
|
||||
import { buildVersionCollectionFields, flattenTopLevelFields, formatNames, toWords } from 'payload'
|
||||
import { fieldAffectsData } from 'payload/shared'
|
||||
import { fieldAffectsData, getLoginOptions } from 'payload/shared'
|
||||
|
||||
import type { ObjectTypeConfig } from './buildObjectType.js'
|
||||
|
||||
@@ -442,10 +442,9 @@ export function initCollections({ config, graphqlResult }: InitCollectionsGraphQ
|
||||
if (!collectionConfig.auth.disableLocalStrategy) {
|
||||
const authArgs = {}
|
||||
|
||||
const canLoginWithEmail =
|
||||
!collectionConfig.auth.loginWithUsername ||
|
||||
collectionConfig.auth.loginWithUsername?.allowEmailLogin
|
||||
const canLoginWithUsername = collectionConfig.auth.loginWithUsername
|
||||
const { canLoginWithEmail, canLoginWithUsername } = getLoginOptions(
|
||||
collectionConfig.auth.loginWithUsername,
|
||||
)
|
||||
|
||||
if (canLoginWithEmail) {
|
||||
authArgs['email'] = { type: new GraphQLNonNull(GraphQLString) }
|
||||
|
||||
@@ -11,6 +11,7 @@ import type { FormState } from 'payload'
|
||||
|
||||
import { Form, FormSubmit, PasswordField, useAuth, useConfig, useTranslation } from '@payloadcms/ui'
|
||||
import { formatAdminURL } from '@payloadcms/ui/shared'
|
||||
import { getLoginOptions } from 'payload/shared'
|
||||
|
||||
import type { LoginFieldProps } from '../LoginField/index.js'
|
||||
|
||||
@@ -36,9 +37,7 @@ export const LoginForm: React.FC<{
|
||||
const collectionConfig = config.collections?.find((collection) => collection?.slug === userSlug)
|
||||
const { auth: authOptions } = collectionConfig
|
||||
const loginWithUsername = authOptions.loginWithUsername
|
||||
const canLoginWithEmail =
|
||||
!authOptions.loginWithUsername || authOptions.loginWithUsername.allowEmailLogin
|
||||
const canLoginWithUsername = authOptions.loginWithUsername
|
||||
const { canLoginWithEmail, canLoginWithUsername } = getLoginOptions(loginWithUsername)
|
||||
|
||||
const [loginType] = React.useState<LoginFieldProps['type']>(() => {
|
||||
if (canLoginWithEmail && canLoginWithUsername) {
|
||||
|
||||
@@ -28,6 +28,9 @@ export const getBaseAuthFields = (authConfig: IncomingAuthType): Field[] => {
|
||||
if (authConfig.loginWithUsername.requireUsername === false) {
|
||||
usernameField.required = false
|
||||
}
|
||||
if (authConfig.loginWithUsername.allowEmailLogin === false) {
|
||||
emailField.unique = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
13
packages/payload/src/auth/getLoginOptions.ts
Normal file
13
packages/payload/src/auth/getLoginOptions.ts
Normal file
@@ -0,0 +1,13 @@
|
||||
import type { Auth } from './types.js'
|
||||
|
||||
export const getLoginOptions = (
|
||||
loginWithUsername: Auth['loginWithUsername'],
|
||||
): {
|
||||
canLoginWithEmail: boolean
|
||||
canLoginWithUsername: boolean
|
||||
} => {
|
||||
return {
|
||||
canLoginWithEmail: !loginWithUsername || loginWithUsername.allowEmailLogin,
|
||||
canLoginWithUsername: Boolean(loginWithUsername),
|
||||
}
|
||||
}
|
||||
@@ -14,6 +14,7 @@ import { APIError } from '../../errors/index.js'
|
||||
import { commitTransaction } from '../../utilities/commitTransaction.js'
|
||||
import { initTransaction } from '../../utilities/initTransaction.js'
|
||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||
import { getLoginOptions } from '../getLoginOptions.js'
|
||||
|
||||
export type Arguments<TSlug extends CollectionSlug> = {
|
||||
collection: Collection
|
||||
@@ -33,8 +34,7 @@ export const forgotPasswordOperation = async <TSlug extends CollectionSlug>(
|
||||
const loginWithUsername = incomingArgs.collection.config.auth.loginWithUsername
|
||||
const { data } = incomingArgs
|
||||
|
||||
const canLoginWithUsername = Boolean(loginWithUsername)
|
||||
const canLoginWithEmail = !loginWithUsername || loginWithUsername.allowEmailLogin
|
||||
const { canLoginWithEmail, canLoginWithUsername } = getLoginOptions(loginWithUsername)
|
||||
|
||||
const sanitizedEmail =
|
||||
(canLoginWithEmail && (incomingArgs.data.email || '').toLowerCase().trim()) || null
|
||||
|
||||
@@ -13,6 +13,7 @@ import { afterRead } from '../../fields/hooks/afterRead/index.js'
|
||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields.js'
|
||||
import { getFieldsToSign } from '../getFieldsToSign.js'
|
||||
import { getLoginOptions } from '../getLoginOptions.js'
|
||||
import isLocked from '../isLocked.js'
|
||||
import { jwtSign } from '../jwt.js'
|
||||
import { authenticateLocalStrategy } from '../strategies/local/authenticate.js'
|
||||
@@ -87,8 +88,7 @@ export const loginOperation = async <TSlug extends CollectionSlug>(
|
||||
? data.username.toLowerCase().trim()
|
||||
: null
|
||||
|
||||
const canLoginWithUsername = Boolean(loginWithUsername)
|
||||
const canLoginWithEmail = !loginWithUsername || loginWithUsername.allowEmailLogin
|
||||
const { canLoginWithEmail, canLoginWithUsername } = getLoginOptions(loginWithUsername)
|
||||
|
||||
// cannot login with email, did not provide username
|
||||
if (!canLoginWithEmail && !sanitizedUsername) {
|
||||
|
||||
@@ -12,6 +12,7 @@ import { commitTransaction } from '../../utilities/commitTransaction.js'
|
||||
import { initTransaction } from '../../utilities/initTransaction.js'
|
||||
import { killTransaction } from '../../utilities/killTransaction.js'
|
||||
import executeAccess from '../executeAccess.js'
|
||||
import { getLoginOptions } from '../getLoginOptions.js'
|
||||
import { resetLoginAttempts } from '../strategies/local/resetLoginAttempts.js'
|
||||
|
||||
export type Arguments<TSlug extends CollectionSlug> = {
|
||||
@@ -32,8 +33,8 @@ export const unlockOperation = async <TSlug extends CollectionSlug>(
|
||||
} = args
|
||||
|
||||
const loginWithUsername = collectionConfig.auth.loginWithUsername
|
||||
const canLoginWithUsername = Boolean(loginWithUsername)
|
||||
const canLoginWithEmail = !loginWithUsername || loginWithUsername.allowEmailLogin
|
||||
|
||||
const { canLoginWithEmail, canLoginWithUsername } = getLoginOptions(loginWithUsername)
|
||||
|
||||
const sanitizedEmail = canLoginWithEmail && (args.data?.email || '').toLowerCase().trim()
|
||||
const sanitizedUsername =
|
||||
|
||||
@@ -3,6 +3,7 @@ import type { JsonObject, Payload } from '../../../index.js'
|
||||
import type { PayloadRequest, SelectType, Where } from '../../../types/index.js'
|
||||
|
||||
import { ValidationError } from '../../../errors/index.js'
|
||||
import { getLoginOptions } from '../../getLoginOptions.js'
|
||||
import { generatePasswordSaltHash } from './generatePasswordSaltHash.js'
|
||||
|
||||
type Args = {
|
||||
@@ -24,9 +25,11 @@ export const registerLocalStrategy = async ({
|
||||
}: Args): Promise<Record<string, unknown>> => {
|
||||
const loginWithUsername = collection?.auth?.loginWithUsername
|
||||
|
||||
const { canLoginWithEmail, canLoginWithUsername } = getLoginOptions(loginWithUsername)
|
||||
|
||||
let whereConstraint: Where
|
||||
|
||||
if (!loginWithUsername) {
|
||||
if (!canLoginWithUsername) {
|
||||
whereConstraint = {
|
||||
email: {
|
||||
equals: doc.email,
|
||||
@@ -37,7 +40,7 @@ export const registerLocalStrategy = async ({
|
||||
or: [],
|
||||
}
|
||||
|
||||
if (doc.email) {
|
||||
if (canLoginWithEmail && doc.email) {
|
||||
whereConstraint.or?.push({
|
||||
email: {
|
||||
equals: doc.email,
|
||||
@@ -67,7 +70,7 @@ export const registerLocalStrategy = async ({
|
||||
throw new ValidationError({
|
||||
collection: collection.slug,
|
||||
errors: [
|
||||
loginWithUsername
|
||||
canLoginWithUsername
|
||||
? {
|
||||
message: req.t('error:usernameAlreadyRegistered'),
|
||||
path: 'username',
|
||||
|
||||
@@ -5,6 +5,7 @@ export {
|
||||
getCookieExpiration,
|
||||
parseCookies,
|
||||
} from '../auth/cookies.js'
|
||||
export { getLoginOptions } from '../auth/getLoginOptions.js'
|
||||
export { getFromImportMap } from '../bin/generateImportMap/getFromImportMap.js'
|
||||
export { parsePayloadComponent } from '../bin/generateImportMap/parsePayloadComponent.js'
|
||||
export { defaults as collectionDefaults } from '../collections/config/defaults.js'
|
||||
|
||||
Reference in New Issue
Block a user