Skip to content

Commit 129e4e2

Browse files
committed
feature(validation) refactor login validation
1 parent 7efdcfe commit 129e4e2

File tree

13 files changed

+142
-175
lines changed

13 files changed

+142
-175
lines changed

composables/useAuth.ts

Lines changed: 11 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -47,43 +47,33 @@ export async function registerWithEmail(
4747
name: string,
4848
email: string,
4949
password: string
50-
): Promise<FormValidation | undefined> {
50+
): Promise<FormValidation> {
5151

5252
try {
53-
const { data, error } = await useFetch<ISession>('/api/auth/register', {
53+
const data = await $fetch<ISession>('/api/auth/register', {
5454
method: 'POST',
55-
body: { data: { username, name, email, password } },
56-
server: false,
57-
key: username + name + email + password
55+
body: { username, name, email, password }
5856
})
5957

60-
if (error.value) {
61-
type ErrorData = {
62-
data: ErrorData
63-
}
64-
65-
const errorData = error.value as unknown as ErrorData
66-
const errors = errorData.data.data as unknown as string
67-
const res = JSON.parse(errors)
68-
const errorMap = new Map<string, { check: InputValidation; }>(Object.entries(res));
69-
70-
return { hasErrors: true, errors: errorMap }
71-
}
72-
7358
if (data) {
7459
useState('user').value = data
7560
await useRouter().push('/topics')
7661
}
77-
} catch (e: any) {
78-
console.log('error: ' + e.toString())
62+
63+
return { hasErrors: false, loggedIn: true }
64+
} catch (error: any) {
65+
const parsedErrors = JSON.parse(error.data.data)
66+
const errorMap = new Map<string, { message: InputValidation; }>(Object.entries(parsedErrors))
67+
return { hasErrors: true, errors: errorMap }
7968
}
8069
}
8170

8271
export async function loginWithEmail(usernameOrEmail: string, password: string): Promise<FormValidation> {
8372
try {
84-
const result = await $fetch<IUser>('/api/auth/login', { method: 'POST', body: { usernameOrEmail: usernameOrEmail, password: password } })
73+
const result = await $fetch('/api/auth/login', { method: 'POST', body: { usernameOrEmail: usernameOrEmail, password: password } })
8574

8675
if (!result?.id) {
76+
debugger
8777
throw Error('something went wrong')
8878
}
8979
useState('user').value = result
@@ -95,5 +85,4 @@ export async function loginWithEmail(usernameOrEmail: string, password: string):
9585
const errorMap = new Map<string, { message: InputValidation; }>(Object.entries(parsedErrors))
9686
return { hasErrors: true, errors: errorMap }
9787
}
98-
9988
}

pages/login.vue

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,15 +7,20 @@ const usernameOrEmail = ref('')
77
const password = ref('')
88
const hasError: Ref<boolean | null> = ref(null)
99
const errorMessage: Ref<string | null> = ref(null)
10-
const errors: Ref<Map<string, { message: InputValidation; }> | undefined> = ref(new Map<string, { message: InputValidation }>())
10+
const errors: Ref<Map<string, { message: InputValidation }> | undefined> = ref(new Map<string, { message: InputValidation }>())
1111
12-
definePageMeta({
13-
middleware: 'guest'
14-
})
12+
// definePageMeta({
13+
// middleware: 'guest'
14+
// })
1515
16-
const postLoginForm = async function () {
17-
const response = await loginWithEmail(usernameOrEmail.value, password.value)
16+
let response: FormValidation
17+
18+
async function postLoginForm() {
19+
response = await loginWithEmail(usernameOrEmail.value, password.value)
1820
errors.value = response.errors
21+
const test = errors.value
22+
debugger
23+
console.log('errors', errors.value)
1924
}
2025
</script>
2126

@@ -38,8 +43,6 @@ const postLoginForm = async function () {
3843

3944
<div v-if="response?.hasErrors && errors"
4045
class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mt-3" role="alert">
41-
<strong class="font-bold">Oops, try again! </strong>
42-
4346
<ul class="block sm:inline">
4447
<li v-for="[key, value] in errors">
4548
{{ value.message }}

pages/register.vue

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,20 @@
11
<script setup lang="ts">
22
import { ref } from "@vue/reactivity";
33
import { registerWithEmail } from "~/composables/useAuth";
4-
import type {Ref} from "vue"
4+
import type { Ref } from "vue"
55
66
const email: Ref<string> = ref('');
77
const password: Ref<string> = ref('');
88
const username: Ref<string> = ref('');
99
const name: Ref<string> = ref('');
10-
const errors: Ref<Map<string, { check: InputValidation; }> | undefined> = ref(new Map<string, { check: InputValidation }>())
11-
let response: Ref<FormValidation|undefined> = ref({ hasErrors: false })
10+
const errors: Ref<Map<string, { message: InputValidation; }> | undefined> = ref(new Map<string, { message: InputValidation }>())
11+
let response: FormValidation
1212
1313
async function postRegisterForm() {
14-
response.value = await registerWithEmail(username.value, name.value, email.value, password.value);
15-
errors.value = response?.value?.errors
14+
response = await registerWithEmail(username.value, name.value, email.value, password.value);
15+
errors.value = response.errors
16+
const test = errors.value
17+
debugger
1618
};
1719
1820
</script>
@@ -34,11 +36,9 @@ async function postRegisterForm() {
3436
</div>
3537
<div v-if="response?.hasErrors && errors"
3638
class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative mt-3" role="alert">
37-
<strong class="font-bold">Oops, try again! </strong>
38-
3939
<ul class="block sm:inline">
4040
<li v-for="[key, value] in errors">
41-
{{ value.check.errorMessage }}
41+
{{ value.message }}
4242
</li>
4343
</ul>
4444
</div>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
import { z, parseBodyAs, } from "@sidebase/nuxt-parse"
2+
import { H3Event } from "h3"
3+
4+
const bodySchema = z.object({
5+
usernameOrEmail: z.string({
6+
required_error: 'username or email required',
7+
})
8+
.min(1, { message: 'username or email required' }),
9+
password: z.string({
10+
required_error: 'password required',
11+
})
12+
.min(8, { message: 'password must be at least 8 characters' })
13+
})
14+
15+
export default async function loginRequest(event: H3Event) {
16+
return await parseBodyAs(event, bodySchema)
17+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { z, parseBodyAs, } from "@sidebase/nuxt-parse"
2+
import { H3Event } from "h3"
3+
4+
const bodySchema = z.object({
5+
username: z.string({
6+
required_error: 'username required',
7+
})
8+
.min(1, { message: 'username required' }),
9+
10+
name: z.string({
11+
required_error: 'name required',
12+
})
13+
.min(1, { message: 'name required' }),
14+
15+
email: z.string({
16+
required_error: 'valid email required',
17+
}).email({ message: 'valid email required' }),
18+
19+
password: z.string({
20+
required_error: 'password required',
21+
})
22+
.min(8, { message: 'password must be at least 8 characters' })
23+
})
24+
25+
export default async function registerRequest(event: H3Event) {
26+
return await parseBodyAs(event, bodySchema)
27+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { H3Event } from "h3"
2+
import { getMappedError } from "~/server/helpers/errorMapper"
3+
4+
export default async function sendDefaultErrorResponse(event: H3Event, erroType: string, error: any) {
5+
const parsedErrors = getMappedError(erroType, error)
6+
return sendError(event, createError({ statusCode: 422, statusMessage: 'Invalid Data Provided', data: parsedErrors }))
7+
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { H3Event } from "h3"
2+
import { getMappedZodErrors } from "~/server/helpers/errorMapper"
3+
4+
export default async function sendZodErrorRespon(event: H3Event, errorData: any) {
5+
const parsedErrors = getMappedZodErrors(errorData)
6+
return sendError(event, createError({ statusCode: 422, statusMessage: 'Invalid Data Provided', data: parsedErrors }))
7+
}

server/api/auth/login.ts

Lines changed: 11 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,21 @@
1-
import { sanitizeUserForFrontend } from '~~/server/services/userService';
1+
import { sanitizeUserForFrontend } from '~/server/services/userService';
22
import bcrypt from 'bcrypt'
33
import { getUserByEmail } from '~/server/database/repositories/userRespository';
44
import { sendError, H3Event } from "h3"
5-
import { makeSession } from '~~/server/services/sessionService';
6-
import { z, parseBodyAs, } from "@sidebase/nuxt-parse"
5+
import { makeSession } from '~/server/services/sessionService';
76
import { ZodError } from "zod"
8-
import { getMappedError, getMappedZodErrors } from '~~/server/helpers/errorMapper';
9-
10-
const bodySchema = z.object({
11-
usernameOrEmail: z.string({
12-
required_error: 'username or email required',
13-
})
14-
.min(1, { message: 'username or email required' }),
15-
password: z.string({
16-
required_error: 'password required',
17-
})
18-
.min(8, { message: 'password must be at least 8 characters' })
19-
})
20-
21-
type RequestBody = z.infer<typeof bodySchema>
7+
import { getMappedError } from '~/server/helpers/errorMapper';
8+
import loginRequest from '~/server/App/formRequests/LoginRequest';
9+
import sendZodErrorResponse from '~/server/App/responses/ZodErrorsResponse';
10+
import sendDefaultErrorResponse from '~/server/App/responses/DefaultErrorsResponse';
2211

2312
const standardAuthError = getMappedError('Authentication', 'Invalid Credentials')
2413

2514
export default eventHandler(async (event: H3Event) => {
2615

27-
let body: RequestBody;
2816
try {
29-
body = await parseBodyAs(event, bodySchema)
30-
31-
const user = await getUserByEmail(body.usernameOrEmail)
17+
const data = await loginRequest(event)
18+
const user = await getUserByEmail(data.usernameOrEmail)
3219

3320
if (user === null) {
3421
return sendError(event, createError({ statusCode: 401, data: standardAuthError }))
@@ -38,7 +25,7 @@ export default eventHandler(async (event: H3Event) => {
3825
return sendError(event, createError({ statusCode: 401, data: standardAuthError }))
3926
}
4027

41-
const isPasswordCorrect = await bcrypt.compare(body.password, user.password)
28+
const isPasswordCorrect = await bcrypt.compare(data.password, user.password)
4229

4330
if (!isPasswordCorrect) {
4431
sendError(event, createError({ statusCode: 401, data: standardAuthError }))
@@ -49,10 +36,9 @@ export default eventHandler(async (event: H3Event) => {
4936
} catch (error: any) {
5037

5138
if (error.data instanceof ZodError) {
52-
const parsedErrors = getMappedZodErrors(error.data)
53-
return sendError(event, createError({ statusCode: 422, statusMessage: 'Unauthenticated', data: parsedErrors }))
39+
return await sendZodErrorResponse(event, error.data)
5440
}
5541

56-
return sendError(event, createError({ statusCode: 500, statusMessage: 'Unauthenticated', data: standardAuthError }))
42+
return await sendDefaultErrorResponse(event, 'Unauthenticated', error)
5743
}
5844
})

server/api/auth/register.ts

Lines changed: 31 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,43 @@
11
import { H3Event, sendError } from 'h3'
22
import bcrypt from 'bcrypt'
33
import { IUser } from '~/types/IUser';
4-
import { validateUser } from '~/server/services/userService'
54
import { createUser } from '~/server/database/repositories/userRespository'
6-
import { makeSession } from '~~/server/services/sessionService';
7-
import { RegistationRequest } from '~~/types/IRegistration'
5+
import { makeSession } from '~/server/services/sessionService';
6+
import { ZodError } from "zod"
7+
import { validateUser } from '~/server/services/userService';
8+
import sendZodErrorResponse from '~/server/App/responses/ZodErrorsResponse';
9+
import sendDefaultErrorResponse from '~/server/App/responses/DefaultErrorsResponse';
10+
import registerRequest from '~/server/App/formRequests/RegisterRequest';
811

9-
export default eventHandler( async(event: H3Event) => {
10-
const body = await readBody(event)
11-
const data = body.data as RegistationRequest
12+
export default eventHandler(async (event: H3Event) => {
13+
try {
14+
const data = await registerRequest(event)
15+
const validation = await validateUser(data)
1216

13-
const validation = await validateUser(data)
17+
if (validation.hasErrors === true && validation.errors) {
18+
const errors = JSON.stringify(Object.fromEntries(validation.errors))
19+
return sendError(event, createError({ statusCode: 422, data: errors }))
20+
}
1421

15-
if (validation.hasErrors === true) {
16-
const errors = JSON.stringify(Object.fromEntries(validation.errors))
17-
return sendError(event, createError({ statusCode: 422, data: errors }))
18-
}
22+
const encryptedPassword: string = await bcrypt.hash(data.password, 10)
1923

20-
const encryptedPassword: string = await bcrypt.hash(data.password, 10)
24+
const userData: IUser = {
25+
username: data.username,
26+
name: data.name,
27+
email: data.email,
28+
loginType: 'email',
29+
password: encryptedPassword
30+
}
2131

22-
const userData: IUser = {
23-
username: data.username,
24-
name: data.name,
25-
email: data.email,
26-
loginType: 'email',
27-
password: encryptedPassword
28-
}
32+
const user = await createUser(userData)
2933

30-
const user = await createUser(userData)
34+
return await makeSession(user, event)
35+
} catch (error: any) {
3136

32-
return await makeSession(user, event)
37+
if (error.data instanceof ZodError) {
38+
return await sendZodErrorResponse(event, error.data)
39+
}
40+
41+
return await sendDefaultErrorResponse(event, 'oops', error)
42+
}
3343
})

server/helpers/errorMapper.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
export function getMappedZodErrors(zodError: any) {
1+
export function mapZodError(zodError: any) {
22
const errors = new Map<string, { message: string }>()
33
JSON.parse(zodError).forEach((zodError: any) => {
44
errors.set(zodError.path[0], { 'message': zodError.message })

server/middleware/serverAuth.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { H3Event } from "h3"
22
import { authCheck } from "../services/userService"
33

4-
export default defineEventHandler(async (event) => {
4+
export default eventHandler(async (event) => {
55

66
const isAllowed = await protectAuthRoute(event)
77

@@ -18,7 +18,7 @@ async function protectAuthRoute(event: H3Event): Promise<boolean> {
1818
'api/ask-jack/delete-question'
1919
]
2020

21-
if (!protectedRoutes.includes(event.req.url)) {
21+
if (!event?.path || !protectedRoutes.includes(event.path)) {
2222
return true
2323
}
2424

0 commit comments

Comments
 (0)