Kaynağa Gözat

improve email and password validation

Aneurin Barker Snook 1 yıl önce
ebeveyn
işleme
e8b10d3d5e

+ 3 - 2
src/account/api.ts

@@ -3,6 +3,7 @@ import type { Context } from '../types'
 import type { RequestHandler } from 'express'
 import type { WithId } from 'mongodb'
 import { prepareAccount } from './http'
+import { password as validatePassword } from '../validate'
 import type { Account, AccountCreate, AccountUpdate } from './types'
 import { http, validate as v } from '@edge/misc-utils'
 
@@ -19,7 +20,7 @@ export function createAccount({ model }: Context): RequestHandler {
   const readRequestData = v.validate<RequestData>({
     account: {
       email: v.email,
-      password: v.seq(v.str, v.minLength(8)),
+      password: validatePassword,
     },
   })
 
@@ -176,7 +177,7 @@ export function updateAccount({ model }: Context): AuthRequestHandler {
   const readRequestData = v.validate<RequestData>({
     account: {
       email: v.seq(v.optional, v.email),
-      password: v.seq(v.optional, v.str, v.minLength(8)),
+      password: v.seq(v.optional, validatePassword),
     },
   })
 

+ 26 - 0
src/validate.ts

@@ -0,0 +1,26 @@
+import { validate } from '@edge/misc-utils'
+
+const passwordConfig = {
+  exprChars: /[A-z]/g,
+  exprNums: /\d/g,
+  exprSymbols: /[!@#$%^&*(){}[\]:;"',./<>?~_-]/g,
+  minLength: 8,
+  minChars: 4,
+  minNumbers: 1,
+  minSymbols: 1,
+}
+
+/** Validate a password, including basic complexity requirements. */
+export const password: validate.ValidateFn = input => {
+  validate.seq(validate.str, validate.minLength(passwordConfig.minLength))
+  const password = input as string
+
+  const chars = password.match(passwordConfig.exprChars)
+  if (!chars || chars.length < passwordConfig.minChars) throw new Error('not enough letters')
+
+  const nums = password.match(passwordConfig.exprNums)
+  if (!nums || nums.length < passwordConfig.minNumbers) throw new Error('not enough numbers')
+
+  const symbols = password.match(passwordConfig.exprSymbols)
+  if (!symbols || symbols.length < passwordConfig.minSymbols) throw new Error('not enough symbols')
+}

+ 31 - 0
web/src/lib/validate.ts

@@ -9,3 +9,34 @@ export function email(input: string | undefined) {
   if (!input) return
   if (!emailRegexp.test(input as string)) return 'invalid email address'
 }
+
+const passwordConfig = {
+  exprChars: /[A-z]/g,
+  exprNums: /\d/g,
+  exprSymbols: /[!@#$%^&*(){}[\]:;"',./<>?~_-]/g,
+  minLength: 8,
+  minChars: 4,
+  minNumbers: 1,
+  minSymbols: 1,
+}
+
+/** Validate a password, including basic complexity requirements. */
+export function password(input: string | undefined) {
+  if (!input) return
+  if (input.length < passwordConfig.minLength) return `Must be at least ${passwordConfig.minLength} characters`
+
+  const chars = input.match(passwordConfig.exprChars)
+  if (!chars || chars.length < passwordConfig.minChars) return 'Not enough letters'
+
+  const nums = input.match(passwordConfig.exprNums)
+  if (!nums || nums.length < passwordConfig.minNumbers) return 'Not enough numbers'
+
+  const symbols = input.match(passwordConfig.exprSymbols)
+  if (!symbols || symbols.length < passwordConfig.minSymbols) return 'Not enough symbols'
+}
+
+/** Validate the length of a password only. */
+export function passwordLength(input: string | undefined) {
+  if (!input) return
+  if (input.length < passwordConfig.minLength) return `Must be at least ${passwordConfig.minLength} characters`
+}

+ 1 - 1
web/src/views/AccountSettingsView.tsx

@@ -27,7 +27,7 @@ function useAccountUpdateForm() {
     email: form.register('email', { validate: validate.email }),
     password: form.register('password', { validate: value => {
       if (!value) return
-      if (value.length < 8) return 'Must be at least 8 characters'
+      return validate.password(value)
     }}),
   }
 

+ 3 - 2
web/src/views/CreateAccountView.tsx

@@ -30,8 +30,9 @@ function useAccountCreateForm() {
       if (!value) return 'Required'
       return validate.email(value)
     }}),
-    password: form.register('password', { validate: {
-      minLength: value => value.length >= 8 || 'Must be at least 8 characters',
+    password: form.register('password', { validate: value => {
+      if (!value) return 'Required'
+      return validate.password(value)
     }}),
   }
 

+ 3 - 2
web/src/views/LoginView.tsx

@@ -29,8 +29,9 @@ function useLoginForm() {
       if (!value) return 'Required'
       return validate.email(value)
     }}),
-    password: form.register('password', { validate: {
-      minLength: value => value.length >= 8 || 'Must be at least 8 characters',
+    password: form.register('password', { validate: value => {
+      if (!value) return 'Required'
+      return validate.passwordLength(value)
     }}),
   }