Skip to content

Commit

Permalink
fix(login) quick fix
Browse files Browse the repository at this point in the history
  • Loading branch information
rohrig committed Nov 16, 2022
1 parent 6285fd2 commit 86ea910
Show file tree
Hide file tree
Showing 7 changed files with 149 additions and 21 deletions.
24 changes: 20 additions & 4 deletions composables/useAuth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,16 +79,32 @@ export async function registerWithEmail(
}
}

export async function loginWithEmail(usernameOrEmail: string, password: string): Promise<boolean> {
export async function loginWithEmail(usernameOrEmail: string, password: string): Promise<FormValidation|undefined> {

try {
const user = await $fetch<IUser>('/api/auth/login', { method: 'POST', body: { usernameOrEmail: usernameOrEmail, password: password } })
const {data: user, error} = await useFetch<IUser>('/api/auth/login', { method: 'POST', body: { usernameOrEmail: usernameOrEmail, password: password } })


if (error.value) {
type ErrorData = {
data: ErrorData
}

const errorData = error.value as unknown as ErrorData
const errors = errorData.data.data as unknown as string
const res = JSON.parse(errors)
const errorMap = new Map<string, { check: InputValidation; }>(Object.entries(res));

return { hasErrors: true, errors: errorMap }
}


console.log(user)
useState('user').value = user
await useRouter().push('/topics')
return true
return undefined
} catch (e) {
return false
return undefined
}

}
34 changes: 25 additions & 9 deletions pages/login.vue
Original file line number Diff line number Diff line change
@@ -1,32 +1,37 @@
<script setup lang="ts">
import { ref } from "@vue/reactivity";
import { loginWithEmail } from "~/composables/useAuth";
import type {Ref} from "vue"
import type { Ref } from "vue"
const usernameOrEmail = ref('')
const password = ref('')
const hasError: Ref<boolean|null> = ref(null)
const errorMessage: Ref<string|null> = ref(null)
const hasError: Ref<boolean | null> = ref(null)
const errorMessage: Ref<string | null> = ref(null)
const errors: Ref<Map<string, { check: InputValidation; }> | undefined> = ref(new Map<string, { check: InputValidation }>())
let response: Ref<FormValidation | undefined> = ref({ hasErrors: false })
definePageMeta({
middleware: 'guest'
})
const postLoginForm = async function () {
const res = await loginWithEmail(usernameOrEmail.value, password.value)
if (!res) {
response.value = await loginWithEmail(usernameOrEmail.value, password.value)
if (!response.value) {
errorMessage.value = 'Invalid Credentials'
hasError.value = true
setTimeout(() => {
hasError.value = false
}, 3000)
}
errors.value = response?.value?.errors
}
</script>

<template>
<div
class="h-screen bg-white dark:bg-black">
<div class="h-screen bg-white dark:bg-black">
<div class="flex items-center justify-center px-4 sm:px-6 lg:px-8">
<div class="max-w-md w-full space-y-8">
<div>
Expand All @@ -42,6 +47,17 @@ const postLoginForm = async function () {
<h2 class="mt-6 py-9 text-center text-3xl font-extrabold text-gray-900 dark:text-gray-400">Sign in</h2>
</div>

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

<div v-if="hasError" class="bg-red-100 border border-red-400 text-red-700 px-4 py-3 rounded relative"
role="alert">
<strong class="font-bold">Oops, try again! </strong>
Expand All @@ -67,8 +83,8 @@ const postLoginForm = async function () {
</div>
<div>
<label for="password" class="sr-only">Password</label>
<input v-model="password" id="password" name="password" type="password" autocomplete="current-password"
required
<input v-model="password" id="password" name="password" required type="password"
autocomplete="current-password"
class="appearance-none rounded-none relative block w-full px-3 py-2 border border-gray-300 placeholder-gray-500 text-gray-900 rounded-b-md focus:outline-none focus:ring-indigo-500 focus:border-indigo-500 focus:z-10 sm:text-sm"
placeholder="Password">
</div>
Expand Down
2 changes: 0 additions & 2 deletions pages/register.vue
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ async function postRegisterForm() {
{{ value.check.errorMessage }}
</li>
</ul>


</div>
<form v-on:submit.prevent class="mt-8 space-y-6" action="#" method="POST">
<input type="hidden" name="remember" value="true" />
Expand Down
32 changes: 30 additions & 2 deletions server/api/auth/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,22 +3,50 @@ import bcrypt from 'bcrypt'
import { getUserByEmail } from '~/server/database/repositories/userRespository';
import { sendError, H3Event } from "h3"
import { makeSession } from '~~/server/services/sessionService';
import { validateLogin } from '~/server/services/userService'

export default eventHandler(async (event: H3Event) => {
const body = await readBody(event)
const usernameOrEmail: string = body.usernameOrEmail
const password: string = body.password

const data = body

const validation = await validateLogin(data)

if (validation.hasErrors === true) {
const errors = JSON.stringify(Object.fromEntries(validation.errors))
return sendError(event, createError({ statusCode: 422, data: errors }))
}

const user = await getUserByEmail(usernameOrEmail)

if (user === null) {
sendError(event, createError({ statusCode: 401, statusMessage: 'Unauthenticated' }))
}

const isPasswordCorrect = bcrypt.compare(password, user.password)
if(user.password == undefined) {
sendError(event, createError({ statusCode: 401, statusMessage: 'Unauthenticated' }))
}

const isPasswordCorrect = await bcrypt.compare(password, user.password)

if (!isPasswordCorrect) {
sendError(event, createError({ statusCode: 401, statusMessage: 'Unauthenticated' }))

const check: InputValidation = {
value: '',
isBlank: false,
lenghtMin8: true,
key: 'Authentication',
hasError: false
}

const errors = new Map<string, { check: InputValidation }>()
errors.set('Authentication', { 'check': check })


const errorsRes = JSON.stringify(Object.fromEntries(new Map()))
sendError(event, createError({ statusCode: 401, data: 'Unauthenticated' }))
}


Expand Down
15 changes: 14 additions & 1 deletion server/services/userService.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { IUser } from '~~/types/IUser';
import { RegistationRequest } from '~~/types/IRegistration';
import { validate } from '~~/server/services/validator';
import { validate, validateSignIn } from '~~/server/services/validator';
import { H3Event } from 'h3';
import { getUserBySessionToken } from './sessionService';
import { isString } from '@vueuse/core';
import { User } from '@prisma/client';
import { LoginRequest } from '~~/types/ILogin';

export async function validateUser(data: RegistationRequest): Promise<FormValidation> {

Expand All @@ -18,6 +19,18 @@ export async function validateUser(data: RegistationRequest): Promise<FormValida
return { hasErrors: false }
}

export async function validateLogin(data: LoginRequest): Promise<FormValidation> {

const errors = await validateSignIn(data)

if (errors.size > 0) {

return { hasErrors: true, errors }
}

return { hasErrors: false }
}

export function sanitizeUserForFrontend(user: User | undefined): User | undefined {

if (!user) {
Expand Down
50 changes: 47 additions & 3 deletions server/services/validator.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { RegistationRequest } from '~~/types/IRegistration';
import { getUserByEmail, getUserByUserName } from '~/server/database/repositories/userRespository';
import { LoginRequest } from '~~/types/ILogin';

export async function validate(data: RegistationRequest) {

const errors = new Map<string, { check: InputValidation }>()

for (const [key, value] of Object.entries(data)) {
let val = await runChecks(key, value)
let val = await validateRegistration(key, value)

if (val.hasError) {
errors.set(key, { 'check': val })
Expand All @@ -16,7 +17,22 @@ export async function validate(data: RegistationRequest) {
return errors
}

async function runChecks(key: string, value: string): Promise<InputValidation> {
export async function validateSignIn(data: LoginRequest) {

const errors = new Map<string, { check: InputValidation }>()

for (const [key, value] of Object.entries(data)) {
let val = await validateLogin(key, value)

if (val.hasError) {
errors.set(key, { 'check': val })
}
}

return errors
}

async function validateRegistration(key: string, value: string): Promise<InputValidation> {
const check: InputValidation = {
value,
isBlank: false,
Expand Down Expand Up @@ -74,4 +90,32 @@ async function runChecks(key: string, value: string): Promise<InputValidation> {
function validateEmail(email: string): boolean {
const re = /\S+@\S+\.\S+/;
return re.test(email);
}
}

async function validateLogin(key: string, value: string): Promise<InputValidation> {
const check: InputValidation = {
value,
isBlank: false,
lenghtMin8: true,
key,
hasError: false
}

if (value == '' || value == null) {
check.isBlank = true
check.hasError = true
check.errorMessage = `${key} is required`
return check
}

if (key == 'password') {
if (value.length < 8) {
check.hasError = true
check.errorMessage = `password must be at least 8 characters`
}
check.lenghtMin8 = false
}


return check
}
13 changes: 13 additions & 0 deletions types/ILogin.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
export type ILoginErrors = {
hasErrors?: string
}

export type LoginResponse = {
hasErrors: boolean,
errors?: ILoginErrors
}

export type LoginRequest = {
usernameOrEmail: string,
password: string
}

0 comments on commit 86ea910

Please sign in to comment.