Show a distinct message when campaign login is temporarily locked.
Users hitting Payload maxLoginAttempts now see lockout guidance instead of the generic invalid-credentials copy.
This commit is contained in:
parent
87c36e4856
commit
bbd9f627f5
3 changed files with 38 additions and 3 deletions
|
|
@ -5,6 +5,10 @@ import { cookies } from 'next/headers'
|
|||
import { redirect } from 'next/navigation'
|
||||
import { getPayload } from 'payload'
|
||||
|
||||
import {
|
||||
CAMPAIGN_ACCOUNT_LOCKED_MESSAGE,
|
||||
CAMPAIGN_LOGIN_INVALID_CREDENTIALS_MESSAGE,
|
||||
} from '@/lib/campaignAuthCopy'
|
||||
import { requiredFormSecret, requiredFormText } from '@/lib/formData'
|
||||
import { campaignLoginSchema, type CampaignLoginInput } from '@/lib/schemas/campaign-login'
|
||||
import {
|
||||
|
|
@ -15,6 +19,12 @@ import {
|
|||
|
||||
export type LoginResult = { error?: string }
|
||||
|
||||
const isLockedAuthError = (error: unknown): boolean =>
|
||||
typeof error === 'object' &&
|
||||
error !== null &&
|
||||
'name' in error &&
|
||||
(error as { name: unknown }).name === 'LockedAuth'
|
||||
|
||||
export const loginCampaign = async (input: CampaignLoginInput): Promise<LoginResult> => {
|
||||
const parsed = campaignLoginSchema.safeParse(input)
|
||||
|
||||
|
|
@ -38,12 +48,15 @@ export const loginCampaign = async (input: CampaignLoginInput): Promise<LoginRes
|
|||
})
|
||||
|
||||
token = result.token
|
||||
} catch {
|
||||
return { error: 'E-mail, celular ou senha inválidos.' }
|
||||
} catch (error) {
|
||||
if (isLockedAuthError(error)) {
|
||||
return { error: CAMPAIGN_ACCOUNT_LOCKED_MESSAGE }
|
||||
}
|
||||
return { error: CAMPAIGN_LOGIN_INVALID_CREDENTIALS_MESSAGE }
|
||||
}
|
||||
|
||||
if (!token) {
|
||||
return { error: 'E-mail, celular ou senha inválidos.' }
|
||||
return { error: CAMPAIGN_LOGIN_INVALID_CREDENTIALS_MESSAGE }
|
||||
}
|
||||
|
||||
await setCampaignAuthCookie(token, payload)
|
||||
|
|
|
|||
|
|
@ -11,6 +11,12 @@ export const CAMPAIGN_FIRST_ACCESS_HINT =
|
|||
|
||||
export const CAMPAIGN_LOGIN_SUBTITLE = 'Use o e-mail ou o celular cadastrado na sua conta.'
|
||||
|
||||
/** Shown when Payload locks the account after maxLoginAttempts (default: 5 / 10 min). */
|
||||
export const CAMPAIGN_ACCOUNT_LOCKED_MESSAGE =
|
||||
'Conta temporariamente bloqueada após várias tentativas. Aguarde alguns minutos e tente de novo.'
|
||||
|
||||
export const CAMPAIGN_LOGIN_INVALID_CREDENTIALS_MESSAGE = 'E-mail, celular ou senha inválidos.'
|
||||
|
||||
/**
|
||||
* Card titles on public campaign auth screens (login / forgot / reset).
|
||||
* `!text-center` beats `[data-theme='campaign'] h1 { text-align: left }` (app shell).
|
||||
|
|
|
|||
|
|
@ -123,4 +123,20 @@ describe('loginCampaign credential mapping', () => {
|
|||
error: 'E-mail, celular ou senha inválidos.',
|
||||
})
|
||||
})
|
||||
|
||||
it('returns a distinct message when Payload locks the account', async () => {
|
||||
const locked = new Error('This user is locked due to having too many failed login attempts.')
|
||||
locked.name = 'LockedAuth'
|
||||
mocks.login.mockRejectedValueOnce(locked)
|
||||
|
||||
await expect(
|
||||
loginCampaign({
|
||||
identifier: 'staff@example.com',
|
||||
password: 'any-password',
|
||||
}),
|
||||
).resolves.toEqual({
|
||||
error:
|
||||
'Conta temporariamente bloqueada após várias tentativas. Aguarde alguns minutos e tente de novo.',
|
||||
})
|
||||
})
|
||||
})
|
||||
|
|
|
|||
Loading…
Reference in a new issue