mirror of
https://github.com/maciejpedzich/racemash.git
synced 2024-11-27 16:15:47 +01:00
Implement authentication module
This commit is contained in:
parent
9713b47b65
commit
84156909ab
16
src/App.vue
16
src/App.vue
@ -1,12 +1,26 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
|
import { useAuth } from './composables/useAuth';
|
||||||
import NavMenu from './components/ui/NavMenu.vue';
|
import NavMenu from './components/ui/NavMenu.vue';
|
||||||
|
|
||||||
|
const { userLoadingFinished } = useAuth();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
<v-app>
|
<v-app>
|
||||||
<NavMenu />
|
<NavMenu />
|
||||||
<v-main>
|
<v-main>
|
||||||
<RouterView />
|
<section
|
||||||
|
v-if="!userLoadingFinished"
|
||||||
|
class="w-100 h-100 pb-4 d-flex justify-center align-center"
|
||||||
|
>
|
||||||
|
<v-progress-circular
|
||||||
|
:size="100"
|
||||||
|
:width="7"
|
||||||
|
color="indigo"
|
||||||
|
indeterminate
|
||||||
|
></v-progress-circular>
|
||||||
|
</section>
|
||||||
|
<RouterView v-show="userLoadingFinished" />
|
||||||
</v-main>
|
</v-main>
|
||||||
</v-app>
|
</v-app>
|
||||||
</template>
|
</template>
|
||||||
|
@ -1,8 +1,19 @@
|
|||||||
<script lang="ts" setup>
|
<script lang="ts" setup>
|
||||||
import { ref } from 'vue';
|
import { ref } from 'vue';
|
||||||
|
import { useRouter } from 'vue-router';
|
||||||
|
|
||||||
|
import { useAuth } from '@/composables/useAuth';
|
||||||
import ThemeSwitch from './ThemeSwitch.vue';
|
import ThemeSwitch from './ThemeSwitch.vue';
|
||||||
|
|
||||||
|
const { isLoggedIn, logOut } = useAuth();
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
const showDrawer = ref(false);
|
const showDrawer = ref(false);
|
||||||
|
|
||||||
|
const logOutAndGoToLogIn = async () => {
|
||||||
|
await logOut();
|
||||||
|
await router.push('/log-in');
|
||||||
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
@ -16,25 +27,23 @@ const showDrawer = ref(false);
|
|||||||
</v-app-bar>
|
</v-app-bar>
|
||||||
<v-navigation-drawer v-model="showDrawer" temporary>
|
<v-navigation-drawer v-model="showDrawer" temporary>
|
||||||
<v-list density="compact" nav>
|
<v-list density="compact" nav>
|
||||||
<v-list-item
|
<v-list-item title="Home" prepend-icon="mdi-home" link to="/" />
|
||||||
title="Home"
|
<template v-if="isLoggedIn">
|
||||||
prepend-icon="mdi-home"
|
<v-list-item title="Vote" prepend-icon="mdi-vote" link to="/vote" />
|
||||||
link
|
<v-list-item
|
||||||
to="/"
|
title="Log out"
|
||||||
></v-list-item>
|
prepend-icon="mdi-logout"
|
||||||
<v-list-item
|
@click="logOutAndGoToLogIn"
|
||||||
title="Vote"
|
/>
|
||||||
prepend-icon="mdi-vote"
|
</template>
|
||||||
link
|
<template v-else>
|
||||||
to="/vote"
|
<v-list-item
|
||||||
></v-list-item>
|
title="Log in"
|
||||||
<v-list-item
|
prepend-icon="mdi-login"
|
||||||
title="Log in"
|
link
|
||||||
prepend-icon="mdi-login"
|
to="/log-in"
|
||||||
link
|
/>
|
||||||
to="/log-in"
|
</template>
|
||||||
></v-list-item>
|
|
||||||
<v-list-item title="Log out" prepend-icon="mdi-logout"></v-list-item>
|
|
||||||
</v-list>
|
</v-list>
|
||||||
</v-navigation-drawer>
|
</v-navigation-drawer>
|
||||||
</template>
|
</template>
|
||||||
|
54
src/composables/useAuth.ts
Normal file
54
src/composables/useAuth.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { ref, computed } from 'vue';
|
||||||
|
import { Models } from 'appwrite';
|
||||||
|
|
||||||
|
import { account } from '@/appwrite';
|
||||||
|
|
||||||
|
const user = ref<Models.User<Models.Preferences> | null>(null);
|
||||||
|
const userLoadingFinished = ref(false);
|
||||||
|
const isLoggedIn = computed(() => !!user.value);
|
||||||
|
|
||||||
|
export function useAuth() {
|
||||||
|
const logIn = (provider: 'github' | 'discord') => {
|
||||||
|
const redirectPath = localStorage.getItem('redirectPath') || '/';
|
||||||
|
const permissionScopes =
|
||||||
|
provider === 'github'
|
||||||
|
? ['read:user', 'user:email']
|
||||||
|
: ['identify', 'email'];
|
||||||
|
|
||||||
|
account.createOAuth2Session(
|
||||||
|
provider,
|
||||||
|
`${location.origin}${redirectPath}`,
|
||||||
|
`${location.origin}/log-in#oauth-error`,
|
||||||
|
permissionScopes
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const loadUser = async () => {
|
||||||
|
try {
|
||||||
|
if (userLoadingFinished.value) return;
|
||||||
|
|
||||||
|
const currentUser = await account.get();
|
||||||
|
user.value = currentUser;
|
||||||
|
} catch (error) {
|
||||||
|
// TODO: Add to globaL Vue error logger and implement snackbar message bus
|
||||||
|
console.error(error);
|
||||||
|
user.value = null;
|
||||||
|
} finally {
|
||||||
|
userLoadingFinished.value = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const logOut = async () => {
|
||||||
|
await account.deleteSession('current');
|
||||||
|
user.value = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
user,
|
||||||
|
userLoadingFinished,
|
||||||
|
isLoggedIn,
|
||||||
|
logIn,
|
||||||
|
loadUser,
|
||||||
|
logOut
|
||||||
|
};
|
||||||
|
}
|
19
src/guards/auth.ts
Normal file
19
src/guards/auth.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import { RouteLocationNormalized, NavigationGuardNext } from 'vue-router';
|
||||||
|
import { useAuth } from '@/composables/useAuth';
|
||||||
|
|
||||||
|
export async function authGuard(
|
||||||
|
to: RouteLocationNormalized,
|
||||||
|
from: RouteLocationNormalized,
|
||||||
|
next: NavigationGuardNext
|
||||||
|
) {
|
||||||
|
const { isLoggedIn, loadUser } = useAuth();
|
||||||
|
|
||||||
|
await loadUser();
|
||||||
|
|
||||||
|
if (!to.meta.authRequired || isLoggedIn.value) {
|
||||||
|
return next();
|
||||||
|
} else {
|
||||||
|
localStorage.setItem('redirectPath', to.fullPath);
|
||||||
|
return next('/log-in');
|
||||||
|
}
|
||||||
|
}
|
@ -1,4 +1,6 @@
|
|||||||
import { createRouter, createWebHistory } from 'vue-router';
|
import { createRouter, createWebHistory } from 'vue-router';
|
||||||
|
|
||||||
|
import { authGuard } from '@/guards/auth';
|
||||||
import Home from '@/views/Home.vue';
|
import Home from '@/views/Home.vue';
|
||||||
|
|
||||||
const routes = [
|
const routes = [
|
||||||
@ -6,6 +8,17 @@ const routes = [
|
|||||||
path: '/',
|
path: '/',
|
||||||
name: 'Home',
|
name: 'Home',
|
||||||
component: Home
|
component: Home
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/log-in',
|
||||||
|
name: 'LogIn',
|
||||||
|
component: () => import('../views/LogIn.vue')
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: '/vote',
|
||||||
|
name: 'Vote',
|
||||||
|
meta: { authRequired: true },
|
||||||
|
component: () => import('../views/Vote.vue')
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
@ -14,4 +27,6 @@ const router = createRouter({
|
|||||||
routes
|
routes
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.beforeEach(authGuard);
|
||||||
|
|
||||||
export default router;
|
export default router;
|
||||||
|
7
src/types.d.ts
vendored
Normal file
7
src/types.d.ts
vendored
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
import 'vue-router';
|
||||||
|
|
||||||
|
declare module 'vue-router' {
|
||||||
|
interface RouteMeta {
|
||||||
|
authRequired?: boolean;
|
||||||
|
}
|
||||||
|
}
|
21
src/views/LogIn.vue
Normal file
21
src/views/LogIn.vue
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
<script lang="ts" setup>
|
||||||
|
import { useAuth } from '@/composables/useAuth';
|
||||||
|
|
||||||
|
const { logIn } = useAuth();
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<template>
|
||||||
|
<section class="w-100 h-100 d-flex flex-column justify-center align-center">
|
||||||
|
<h1 class="text-h3 mb-3">Log in via:</h1>
|
||||||
|
<v-container>
|
||||||
|
<v-row align="center" justify="center">
|
||||||
|
<v-col cols="auto">
|
||||||
|
<v-btn size="large" @click="logIn('github')">GitHub</v-btn>
|
||||||
|
</v-col>
|
||||||
|
<v-col cols="auto">
|
||||||
|
<v-btn size="large" @click="logIn('discord')">Discord</v-btn>
|
||||||
|
</v-col>
|
||||||
|
</v-row>
|
||||||
|
</v-container>
|
||||||
|
</section>
|
||||||
|
</template>
|
3
src/views/Vote.vue
Normal file
3
src/views/Vote.vue
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
<template>
|
||||||
|
<h1>Vote</h1>
|
||||||
|
</template>
|
Loading…
Reference in New Issue
Block a user