Skip to content

Commit 28145ac

Browse files
jmikrutkendelljoseph
authored andcommitted
feat: exports additional login helper utils (#12309)
Exports a few utilities that are used internally to the login operation, but could be helpful for others building plugins. Specifically: - `isUserLocked` - a check to ensure that a given user is not locked due to too many invalid attempts - `checkLoginPermissions` - checks to see that the user is not locked as well as that it is properly verified, if applicable - `jwtSign` - Payload's internal JWT signing approach - `getFieldsToSign` - reduce down a document's fields for JWT creation based on collection config settings - `incrementLoginAttempts` / `resetLoginAttempts` - utilities to handle both failed and successful login attempts - `UnverifiedEmail` - an error that could be thrown if attempting to log in to an account without prior successful email verification
1 parent de7814a commit 28145ac

File tree

4 files changed

+49
-22
lines changed

4 files changed

+49
-22
lines changed

packages/payload/src/auth/isLocked.ts

Lines changed: 0 additions & 7 deletions
This file was deleted.
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
export const isUserLocked = (date: number): boolean => {
2+
if (!date) {
3+
return false
4+
}
5+
return date > Date.now()
6+
}

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

Lines changed: 34 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import type {
33
AuthOperationsFromCollectionSlug,
44
Collection,
55
DataFromCollectionSlug,
6+
SanitizedCollectionConfig,
67
} from '../../collections/config/types.js'
78
import type { CollectionSlug } from '../../index.js'
89
import type { PayloadRequest, Where } from '../../types/index.js'
@@ -21,7 +22,7 @@ import { killTransaction } from '../../utilities/killTransaction.js'
2122
import sanitizeInternalFields from '../../utilities/sanitizeInternalFields.js'
2223
import { getFieldsToSign } from '../getFieldsToSign.js'
2324
import { getLoginOptions } from '../getLoginOptions.js'
24-
import isLocked from '../isLocked.js'
25+
import { isUserLocked } from '../isUserLocked.js'
2526
import { jwtSign } from '../jwt.js'
2627
import { authenticateLocalStrategy } from '../strategies/local/authenticate.js'
2728
import { incrementLoginAttempts } from '../strategies/local/incrementLoginAttempts.js'
@@ -42,6 +43,32 @@ export type Arguments<TSlug extends CollectionSlug> = {
4243
showHiddenFields?: boolean
4344
}
4445

46+
type CheckLoginPermissionArgs = {
47+
collection: SanitizedCollectionConfig
48+
loggingInWithUsername?: boolean
49+
req: PayloadRequest
50+
user: any
51+
}
52+
53+
export const checkLoginPermission = ({
54+
collection,
55+
loggingInWithUsername,
56+
req,
57+
user,
58+
}: CheckLoginPermissionArgs) => {
59+
if (!user) {
60+
throw new AuthenticationError(req.t, Boolean(loggingInWithUsername))
61+
}
62+
63+
if (collection.auth.verify && user._verified === false) {
64+
throw new UnverifiedEmail({ t: req.t })
65+
}
66+
67+
if (isUserLocked(new Date(user.lockUntil).getTime())) {
68+
throw new LockedAuth(req.t)
69+
}
70+
}
71+
4572
export const loginOperation = async <TSlug extends CollectionSlug>(
4673
incomingArgs: Arguments<TSlug>,
4774
): Promise<{ user: DataFromCollectionSlug<TSlug> } & Result> => {
@@ -184,21 +211,16 @@ export const loginOperation = async <TSlug extends CollectionSlug>(
184211
where: whereConstraint,
185212
})
186213

187-
if (!user) {
188-
throw new AuthenticationError(req.t, Boolean(canLoginWithUsername && sanitizedUsername))
189-
}
190-
191-
if (args.collection.config.auth.verify && user._verified === false) {
192-
throw new UnverifiedEmail({ t: req.t })
193-
}
214+
checkLoginPermission({
215+
collection: collectionConfig,
216+
loggingInWithUsername: Boolean(canLoginWithUsername && sanitizedUsername),
217+
req,
218+
user,
219+
})
194220

195221
user.collection = collectionConfig.slug
196222
user._strategy = 'local-jwt'
197223

198-
if (isLocked(new Date(user.lockUntil).getTime())) {
199-
throw new LockedAuth(req.t)
200-
}
201-
202224
const authResult = await authenticateLocalStrategy({ doc: user, password })
203225

204226
user = sanitizeInternalFields(user)

packages/payload/src/index.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,10 @@ import { traverseFields } from './utilities/traverseFields.js'
8989

9090
export { default as executeAccess } from './auth/executeAccess.js'
9191
export { executeAuthStrategies } from './auth/executeAuthStrategies.js'
92+
export { extractAccessFromPermission } from './auth/extractAccessFromPermission.js'
93+
export { getAccessResults } from './auth/getAccessResults.js'
94+
export { getFieldsToSign } from './auth/getFieldsToSign.js'
95+
export { getLoginOptions } from './auth/getLoginOptions.js'
9296

9397
export interface GeneratedTypes {
9498
authUntyped: {
@@ -977,13 +981,12 @@ interface RequestContext {
977981
// eslint-disable-next-line @typescript-eslint/no-empty-object-type
978982
export interface DatabaseAdapter extends BaseDatabaseAdapter {}
979983
export type { Payload, RequestContext }
980-
export { extractAccessFromPermission } from './auth/extractAccessFromPermission.js'
981-
export { getAccessResults } from './auth/getAccessResults.js'
982-
export { getFieldsToSign } from './auth/getFieldsToSign.js'
983984
export * from './auth/index.js'
985+
export { jwtSign } from './auth/jwt.js'
984986
export { accessOperation } from './auth/operations/access.js'
985987
export { forgotPasswordOperation } from './auth/operations/forgotPassword.js'
986988
export { initOperation } from './auth/operations/init.js'
989+
export { checkLoginPermission } from './auth/operations/login.js'
987990
export { loginOperation } from './auth/operations/login.js'
988991
export { logoutOperation } from './auth/operations/logout.js'
989992
export type { MeOperationResult } from './auth/operations/me.js'
@@ -994,6 +997,8 @@ export { resetPasswordOperation } from './auth/operations/resetPassword.js'
994997
export { unlockOperation } from './auth/operations/unlock.js'
995998
export { verifyEmailOperation } from './auth/operations/verifyEmail.js'
996999
export { JWTAuthentication } from './auth/strategies/jwt.js'
1000+
export { incrementLoginAttempts } from './auth/strategies/local/incrementLoginAttempts.js'
1001+
export { resetLoginAttempts } from './auth/strategies/local/resetLoginAttempts.js'
9971002
export type {
9981003
AuthStrategyFunction,
9991004
AuthStrategyFunctionArgs,
@@ -1201,6 +1206,7 @@ export {
12011206
MissingFile,
12021207
NotFound,
12031208
QueryError,
1209+
UnverifiedEmail,
12041210
ValidationError,
12051211
ValidationErrorName,
12061212
} from './errors/index.js'

0 commit comments

Comments
 (0)