From d63f69645ed25e25b36e5e9cba7f3270f59d6119 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Roso=C5=82owski?= <87621210+rr-adam@users.noreply.github.com> Date: Mon, 9 Sep 2024 11:47:57 +0200 Subject: [PATCH 1/2] GitHub login flow --- general/components/Header/AuthLoginModal.vue | 37 ++++++ general/pages/connect/github.vue | 128 +++++++++++++++++++ 2 files changed, 165 insertions(+) create mode 100644 general/pages/connect/github.vue diff --git a/general/components/Header/AuthLoginModal.vue b/general/components/Header/AuthLoginModal.vue index 0fb89758..9e95da1e 100644 --- a/general/components/Header/AuthLoginModal.vue +++ b/general/components/Header/AuthLoginModal.vue @@ -64,6 +64,21 @@ Create an account + @@ -142,5 +157,27 @@ const handleSubmit = async () => { } }; +const connectGithub = () => { + const githubConnectUrl = `${baseURL()}/api/connect/github`; + + window.location.href = githubConnectUrl; +}; + const modalOpen = computed(() => authModalStore.authModal === 'login'); + + diff --git a/general/pages/connect/github.vue b/general/pages/connect/github.vue new file mode 100644 index 00000000..11eb0717 --- /dev/null +++ b/general/pages/connect/github.vue @@ -0,0 +1,128 @@ + + + + + From d7de9115ba459cd07f0a70973c626fed357e3b9e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Roso=C5=82owski?= <87621210+rr-adam@users.noreply.github.com> Date: Fri, 13 Sep 2024 15:12:19 +0200 Subject: [PATCH 2/2] Store route and fetch user role --- general/components/Header/AuthLoginModal.vue | 7 ++- general/pages/connect/github.vue | 15 ++++-- general/stores/auth.ts | 56 ++++++++++++++++++-- 3 files changed, 69 insertions(+), 9 deletions(-) diff --git a/general/components/Header/AuthLoginModal.vue b/general/components/Header/AuthLoginModal.vue index 9e95da1e..ba50869c 100644 --- a/general/components/Header/AuthLoginModal.vue +++ b/general/components/Header/AuthLoginModal.vue @@ -95,6 +95,8 @@ const toastStore = useToastStore(); const authStore = useAuthStore(); const authModalStore = useAuthModalStore(); +const route = useRoute(); + const identifier = ref(''); const password = ref(''); const error = ref(null); @@ -132,8 +134,7 @@ const handleSubmit = async () => { }); if (response.jwt && response.user) { - authStore.setJwt(response.jwt); - authStore.setUserData(response.user); + await authStore.loginAndFetchProfile(response.jwt, response.user); toastStore.addToast('You are logged in.'); hideModal(); } else { @@ -160,6 +161,8 @@ const handleSubmit = async () => { const connectGithub = () => { const githubConnectUrl = `${baseURL()}/api/connect/github`; + authStore.setRedirectLink(route.fullPath); + window.location.href = githubConnectUrl; }; diff --git a/general/pages/connect/github.vue b/general/pages/connect/github.vue index 11eb0717..69f9a366 100644 --- a/general/pages/connect/github.vue +++ b/general/pages/connect/github.vue @@ -68,11 +68,14 @@ onMounted(async () => { ); if (response.jwt && response.user) { - authStore.setJwt(response.jwt); - authStore.setUserData(response.user); + await authStore.loginAndFetchProfile(response.jwt, response.user); toastStore.addToast('Successfully logged in with GitHub.'); statusMessage.value = 'Authentication successful. Redirecting...'; - setTimeout(() => router.push('/ontology'), 2000); + + const redirectUrl = authStore.redirectLink || '/ontology'; + authStore.clearRedirectLink(); + + setTimeout(() => router.push(redirectUrl), 2000); } else { throw new Error('Invalid response from server'); } @@ -86,7 +89,11 @@ const handleError = (message: string) => { statusMessage.value = 'Authentication failed. Redirecting...'; toastStore.addToast(`GitHub authentication failed!`, 'error'); authStore.clear(); - setTimeout(() => router.push('/'), 2000); + + const redirectUrl = authStore.redirectLink || '/'; + authStore.clearRedirectLink(); + + setTimeout(() => router.push(redirectUrl), 2000); }; diff --git a/general/stores/auth.ts b/general/stores/auth.ts index 97660a97..51daaf52 100644 --- a/general/stores/auth.ts +++ b/general/stores/auth.ts @@ -1,4 +1,5 @@ import { defineStore } from 'pinia'; +import { useRuntimeConfig } from '#app'; export interface UserData { id: number; @@ -22,6 +23,7 @@ export interface UserData { interface AuthState { jwt: string | null; user: UserData | null; + redirectLink: string | null; } export const useAuthStore = defineStore({ @@ -29,7 +31,8 @@ export const useAuthStore = defineStore({ state: (): AuthState => { return { jwt: null, - user: null + user: null, + redirectLink: null }; }, actions: { @@ -39,9 +42,56 @@ export const useAuthStore = defineStore({ setUserData(user: UserData) { this.user = user; }, + setRedirectLink(link: string) { + this.redirectLink = link; + }, + clearRedirectLink() { + this.redirectLink = null; + }, + async fetchFullProfile() { + if (!this.jwt) { + throw new Error('No JWT token available'); + } + + const runtimeConfig = useRuntimeConfig(); + const baseURL = + typeof window !== 'undefined' + ? window.location.origin + `${runtimeConfig.public.strapiBasePath}` + : `${runtimeConfig.public.strapiBaseUrl}`; + + try { + const response = await $fetch( + `${baseURL}/api/users/me?populate=role`, + { + method: 'GET', + headers: { + Authorization: `Bearer ${this.jwt}` + } + } + ); + + if (response) { + this.setUserData(response); + } else { + throw new Error('Invalid response from server'); + } + } catch (error) { + console.error('Error fetching full profile:', error); + throw error; + } + }, + async loginAndFetchProfile( + jwt: string, + initialUserData: Partial + ) { + this.setJwt(jwt); + this.setUserData({ ...initialUserData } as UserData); + await this.fetchFullProfile(); + }, clear() { this.jwt = null; this.user = null; + this.redirectLink = null; } }, persist: { @@ -49,7 +99,7 @@ export const useAuthStore = defineStore({ const runtimeConfig = useRuntimeConfig(); return `ontoviewer-auth-${runtimeConfig.public.ontologyName}`; }, - storage: process.client ? localStorage : undefined, - paths: ['jwt', 'user'] + storage: import.meta.client ? localStorage : undefined, + paths: ['jwt', 'user', 'redirectLink'] } });