"disableLocalStrategy: true" still asking for email or nickname
Environment Info
"next": "15.0.0-canary.104",
"payload": "beta",
"nodejs": v18.20.4
Describe the Bug
I want to create a custom auth for trainees that can login with their firstname, lastname and trainee_id. There is no need for password, email or nicknames. I've find out that disableLocalStrategy set to true is ignored and still requires email/nickname and password. If i'm doing something wrong, please tell me how to fix it?
Reproduction Steps
import type {CollectionConfig} from 'payload'
export default {
slug: 'trainees',
labels: {
singular: 'Azubi',
plural: 'Azubis',
},
auth: {
disableLocalStrategy: true,
strategies: [
{
identityField: 'trainee_id',
name: 'custom-strategy',
async authenticate({headers, payload}) {
const {docs} = await payload.find({
collection: 'trainees',
where: {
name_first: {equals: headers.get('name_first')},
name_last: {equals: headers.get('name_last')},
trainee_id: {equals: headers.get('trainee_id')},
},
})
return {user: docs[0]}
},
},
],
},
fields: [
{
name: 'name_first',
label: 'Vorname',
type: 'text',
required: true,
},
{
name: 'name_last',
label: 'Nachname',
type: 'text',
required: true,
},
{
name: 'trainee_id',
label: 'Matrikelnummer',
type: 'text',
required: true,
},
],
} as const satisfies CollectionConfig
is called by
const result = await payload.login({
collection: 'trainees',
data: {
name_first: 'Hassan',
name_last: 'Altay',
trainee_id: '43523452345',
},
})
console.log(result)
causes
dock-payload | ⨯ node_modules/payload/dist/auth/operations/login.js (56:1) @ loginOperation
dock-payload | ⨯ ValidationError: The following field is invalid: email
dock-payload | at async $$ACTION_0 (./src/app/logic/actions.ts:52:20)
dock-payload | digest: "1954918542"
dock-payload | Cause: {
dock-payload | collection: 'trainees',
dock-payload | errors: [ { field: 'email', message: 'This field is required.' } ]
dock-payload | }
dock-payload | 54 | // cannot login with username, did not provide email
dock-payload | 55 | if (!canLoginWithUsername && !sanitizedEmail) {
dock-payload | > 56 | throw new ValidationError({
dock-payload | | ^
dock-payload | 57 | collection: collectionConfig.slug,
dock-payload | 58 | errors: [
dock-payload | 59 | {
can somebody help me with this, please? :)
hi , you are reading headers in custom strategies ?!!
hi , you are reading headers in custom strategies ?!!
yes, i can pass my own headers as you see
name_first: {equals: headers.get('name_first')},
name_last: {equals: headers.get('name_last')},
trainee_id: {equals: headers.get('trainee_id')},
but this parameter disableLocalStrategy: true doesn't have any effect, the payload still requires email/nickname and password field
hi,
i didn't see that your setting headers, it seems strategy are an array so they are fired one after another till any of them fulfills the record , i am build a custom strategy, looking forward to a solution too
I think the custom strategies is not supposed to use with login auth operation. It's for accessing the API directly without doing the login auth operation. I ended up just creating custom login root endpoint based on the source here https://github.com/payloadcms/payload/blob/v2.28.0/packages/payload/src/auth/operations/login.ts#L43
import { CookieOptions, Response } from 'express'
import jwt from 'jsonwebtoken'
import { PayloadRequest } from 'payload/types'
const getCookieExpiration = (seconds = 7200) => {
const currentTime = new Date()
currentTime.setSeconds(currentTime.getSeconds() + seconds)
return currentTime
}
export async function handleCustomLogin(req: PayloadRequest, res: Response) {
const { payload, body: credentials } = req
const user = ... // get user here
if (!user) return res.status(401).send('User not found')
const { config, secret } = payload
const collectionConfig = payload.collections['users'].config
const fieldsToSign = {
id: user.id,
collection: 'users',
email: user.email,
}
const token = jwt.sign(fieldsToSign, secret, {
expiresIn: collectionConfig.auth.tokenExpiration,
})
const cookieOptions: CookieOptions = {
domain: undefined,
expires: getCookieExpiration(collectionConfig.auth.tokenExpiration),
httpOnly: true,
path: '/',
sameSite: collectionConfig.auth.cookies.sameSite,
secure: collectionConfig.auth.cookies.secure,
}
if (collectionConfig.auth.cookies.domain) cookieOptions.domain = collectionConfig.auth.cookies.domain
res.cookie(`${config.cookiePrefix}-token`, token, cookieOptions)
const result = {
exp: (jwt.decode(token) as jwt.JwtPayload).exp,
token,
user,
}
return res.json(result)
}
Please be aware that I strip a lot of function from the upstream source, so it might cause some unintended effect
This issue has been marked as stale due to lack of activity.
To keep this issue open, please indicate that it is still relevant in a comment below.
as @ffd114 said, the local operation payload.login is not meant to be used with disableLocalStrategy: true. It will throw an error here: https://github.com/payloadcms/payload/blob/main/packages/payload/src/auth/operations/login.ts#L71-L73
This issue has been automatically locked. Please open a new issue if this issue persists with any additional detail.