Skip to content

Commit 832e109

Browse files
GKitsmohmans
andauthored
Implement nuxt-auth (#219) (#220)
* chore: install nuxt auth * chore: update nuxt-auth and add configs * refactor: rename legacy useAuth composable * refactor(Login): update to use nuxt-auth * feat: add composable to fetch member data * chore: add JSDocs type declarator to useFetchMember * feat(useMember): save member data to state * refactor(userWidget): update to use the new composable * refactor(LoginForm): update auth handling * fix(LoginForm): a11y issues in form inputs * refactor(logout): use nuxt-auth logout * refactor: login page middleware * refactor(middleware): turn auth middleware into a global one and refactor its logic * chore: update jwt cookie name * refactor: remove register middleware * refactor: remove state composoable in favor of useMember * feat: add custom setters for userId and memberData composables * fix: handle invalid stored userId in the auth middleware * fix(member-banner): error handling when updaing info * refactor: use bearer token in update member info and settings endpoint * fix(member-banner): error handling when updaing avatar/cover * fix: check for cached member data first before attempting to fetch it * fix: update auth middleware to skip settings when unauth * refactor(api): use bearer token instead of cookie for crud requests * fix(user-widget): styling issues * refactor: refresh member data using the new composable * refactor: throw error if attempted to fetch member without providing id and handle it in the middleware * fix: remove auth middleware from member page * chore: cookie name * chore: burn unwanted file * fix: update member data in storage iff they're the logged in member * refactor: add logoutMember composable * refactor: remove localStorage entirely * refactor: auth middleware * fix: turn the isUserLogged into a computed property * fix: add memberData computed property * chore: set cookie max age to 1 day Co-authored-by: Mo Mansour <[email protected]>
1 parent 02431b5 commit 832e109

34 files changed

+6513
-18943
lines changed

.env.sample

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
# endpoint for API
22
NUXT_PUBLIC_COMMUNITY_API_URL=
3+
AUTH_ORIGIN=
34
NUXT_PUBLIC_TARGET_ENV=
45
NUXT_PUBLIC_BUILD_COMMIT_SHA=
56
NUXT_PUBLIC_BUILD_NUMBER=

components/Form/AppControlInput.vue

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,14 @@
3939
@focusout="state.pwActive = false"
4040
/>
4141
<span v-if="props.showPasswordIcon && props.inputType === 'password'">
42-
<i
42+
<button
4343
class="eye-icon"
4444
:class="[
4545
state.showPassword ? 'hide' : 'show',
4646
state.pwActive ? 'focusBg' : '',
4747
]"
48-
@click="state.showPassword = !state.showPassword"
49-
></i>
48+
@click.prevent="state.showPassword = !state.showPassword"
49+
></button>
5050
</span>
5151
</div>
5252
<div

components/MobileMenuButton.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
<template>
22
<div class="lg:hidden flex items-center w-full justify-end">
3-
<button class="outline-none mr-4" aria-label="Mobile Menu" @click="OnClick">
3+
<button class="outline-none mr-4 z-20 bg-community-black-darker" aria-label="Mobile Menu" @click="OnClick">
44
<img
55
v-if="props.isOpen"
66
class="w-9 h-9"
@@ -9,7 +9,7 @@
99
/>
1010
<img v-else class="w-9 h-9" src=" /icons/bars-solid.svg" alt="" />
1111
</button>
12-
<UserWidget v-if="useAuth().value" />
12+
<UserWidget v-if="isAuth().value" />
1313
</div>
1414
</template>
1515

components/MobileMenuItems.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@
5353
id="login"
5454
to="/login"
5555
class="nav-link"
56-
v-if="!useAuth().value"
56+
v-if="!isAuth().value"
5757
>Sign In</NuxtLink
5858
>
5959
</li>

components/PageHeader.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
id="login"
3838
to="/login"
3939
class="nav-link"
40-
v-if="!useAuth().value"
40+
v-if="!isAuth().value"
4141
>Sign In</NuxtLink
4242
>
4343
<UserWidget v-else />

components/UserWidget.vue

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,29 @@
11
<template>
2-
<div v-if="useMember().value" class="widget-container">
2+
<div v-if="memberData?.member" class="widget-container">
33
<img
44
@click="togglePopup"
55
class="avatar"
66
alt="avatar"
77
:src="
8-
useMember().value.avatar_url
9-
? useMember().value.avatar_url
8+
memberData?.member.avatar_url
9+
? memberData?.member.avatar_url
1010
: '/images/placeholders/avatar.png'
1111
"
1212
/>
1313
<div v-if="state.isOpen" class="user-menu">
1414
<div class="widget-arrow"></div>
1515
<p class="user-name">
16-
{{ useMember().value.first_name_en }} {{ useMember().value.last_name_en }}
16+
{{ memberData?.member.first_name_en }} {{ memberData?.member.last_name_en }}
1717
</p>
1818
<div class="flex flex-row justify-between mb-4">
19-
<p v-if="useMember().value.type !== 0"
19+
<p v-if="memberData?.member.type !== 0"
2020
class="user-info">
2121
<div class="badge-color"></div>
2222
JOSA Member
2323
</p>
24-
<p v-if="useMember().value.josa_member_id && useMember().value.type !== 0"
24+
<p v-if="memberData?.member.josa_member_id && memberData?.member.type !== 0"
2525
class="user-info">
26-
#{{ useMember().value.type }}-{{ useMember().value.josa_member_id }}
26+
#{{ memberData?.member.type }}-{{ memberData?.member.josa_member_id }}
2727
</p>
2828
</div>
2929
<div class="divider-dotted"></div>
@@ -44,19 +44,19 @@
4444
</div>
4545
</div>
4646
</div>
47+
<div v-else class="loader"></div>
4748
</template>
4849

4950
<script setup>
51+
52+
const memberData = useMemberData()
5053
const togglePopup = () => {
5154
state.isOpen = !state.isOpen
5255
}
5356
54-
const signOut = async () => {
57+
const logout = async () => {
5558
togglePopup()
56-
await useFetch('/api/logout')
57-
useAuth().value = false
58-
userId().value = null
59-
navigateTo('/')
59+
logoutMember()
6060
}
6161
6262
const state = reactive({
@@ -81,7 +81,7 @@ const widget = reactive({
8181
title: 'Sign out',
8282
to: '/',
8383
icon: 'ic-logout',
84-
onClick: signOut
84+
onClick: logout
8585
}
8686
]
8787
})
@@ -92,7 +92,7 @@ const widget = reactive({
9292
@apply flex flex-col items-end justify-between;
9393
}
9494
.avatar {
95-
@apply w-10 rounded;
95+
@apply w-10 h-10 object-cover rounded;
9696
}
9797
.avatar:hover {
9898
@apply cursor-pointer

components/home/HeroBanner.vue

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
</div>
2828

2929
<NuxtLink
30-
v-if="!useAuth().value"
30+
v-if="!isAuth().value"
3131
class="button-flat button-blue-full"
3232
to="/register"
3333
>

components/login/Form.vue

Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@
2727
>
2828
Password
2929
</FormAppControlInput>
30-
<NuxtLink @click="$emit('forgotPassword', true)">
30+
<NuxtLink @click="$emit('forgotPassword', true)" href="#">
3131
Forgot Password?
3232
</NuxtLink>
3333
<FormAppButton
@@ -46,6 +46,7 @@
4646
</template>
4747
<script setup>
4848
const emit = defineEmits(['forgotPassword'])
49+
const { data, signIn } = useAuth()
4950
5051
const form = reactive({
5152
email: '',
@@ -56,29 +57,28 @@ const state = reactive({
5657
error: false
5758
})
5859
const login = async() => {
60+
state.error = false
5961
state.loading = true
6062
61-
// TODO: refresh data on submitting, or use $fetch.raw()
62-
await $fetch('/api/login', {
63-
method: "POST",
64-
body: JSON.stringify({
65-
"email": form.email,
66-
"password": form.password
67-
}),
68-
onResponse({response}) {
69-
if(response.ok) {
70-
state.loading = false
71-
const {first_name_en, last_name_en, id, avatar_url, type, josa_member_id} = response._data
72-
useMember().value = {first_name_en, last_name_en, id, avatar_url, type, josa_member_id}
73-
useAuth().value = true
74-
userId().value = response._data.id
75-
navigateTo('/')
76-
}
77-
},
78-
onResponseError({response}) {
79-
state.loading = false
80-
state.error = true
63+
await signIn({
64+
email: form.email,
65+
password: form.password
66+
}, {
67+
callbackUrl: "/"
68+
})
69+
.catch((error) => {
70+
console.error("Error while signing in: ", error)
71+
state.error = true
72+
})
73+
.finally(async () => {
74+
const member = data.value
75+
if (member?.id && member.username === form.email) {
76+
console.info(`${member.username} is logged in!`)
77+
updateUserId(member.id)
78+
isAuth().value = true
79+
await useFetchMember()
8180
}
81+
state.loading = false
8282
})
8383
}
8484
</script>

components/login/forgotPassword.vue

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@
3636
<div>Send password reset email</div>
3737
</div>
3838
</FormAppButton>
39-
<NuxtLink @click="$emit('forgotPassword', false)">
39+
<NuxtLink @click="$emit('forgotPassword', false)" href="#">
4040
Back to sign in
4141
</NuxtLink>
4242
</form>
@@ -45,7 +45,7 @@
4545
An email with a link to reset your password was sent to {{ form.email }}
4646
</Message>
4747
<div class="flex flex-row justify-between mt-4">
48-
<NuxtLink @click="$emit('forgotPassword', false)">
48+
<NuxtLink @click="$emit('forgotPassword', false)" href="#">
4949
Login
5050
</NuxtLink>
5151
<NuxtLink to="/">

components/member/Banner.vue

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -208,16 +208,17 @@ const uploadImage = async (event, imageType) => {
208208
state.images.avatar.url = avatar_url
209209
? avatar_url + '?last-updated=' + Date.now()
210210
: props.member.avatar_url
211-
useMember().value[`${imageType}_url`] = state.images[imageType].url
211+
useMemberData().value[`${imageType}_url`] = state.images[imageType].url
212+
state.success = true
212213
}
213214
})
214215
.catch((error) => {
215-
state.loading = false
216216
state.error = true
217217
state.success = false
218218
})
219219
.finally(() => {
220220
updateGeneralInfo()
221+
state.loading = false
221222
})
222223
}
223224
@@ -236,17 +237,14 @@ const updateGeneralInfo = async (event) => {
236237
body: JSON.stringify(bodyData),
237238
onResponse({ response }) {
238239
if (response._data) {
239-
state.error = false
240-
state.success = true
241240
state.loading = false
242241
}
243242
emit('updateMember')
244243
},
245244
onResponseError({ response }) {
246-
state.error = false
247-
state.success = true
248-
state.loading = false
249-
console.log('something went wrong', response._data.message)
245+
state.error = true
246+
state.success = false
247+
console.error('something went wrong', response._data.message)
250248
},
251249
})
252250
showUpdateInfoForm.value = !showUpdateInfoForm

0 commit comments

Comments
 (0)