firebase auth with vue router - firebase

I have the authorization check code and it works. Now I need to implement path protection for an unauthorized user.
The problem is that the function in the storage does not have time to work out as it is already necessary to go along the path. AuthState and LoginStatus
try do it from getters, get actual state and try get data from state, but nothing happened
When I reload the page or clear the cache everything resets
//STORE
// call it first from app in created()
state: () => ({
isAuthReady: null,
}),
async AuthState({ dispatch }) {
await auth.onAuthStateChanged((userFirebase) => {
dispatch("LoginStatus", userFirebase);
});
},
LoginStatus({ commit, dispatch }, user) {
//console.log(user)
if (user) {
commit("setAuthReady", true);
commit("setUser", user);
dispatch("UserProfile", user);
dispatch("isAdmin");
} else {
// User is signed out
// ...
}
},
//ROUTER
{
path: "/admin",
component: () => import("#/pages/adminPage/admin"),
meta: { requiresAuth: true },
}
router.beforeEach(async (to, from, next) => {
if (to.meta.requiresAuth) {
if (store.state.user.userInfo.length || store.state.user.userInfo.id) {
next();
} else {
await store.dispatch("auth/openLoginForm");
next("/");
}
} else next();
});

I don’t know if I did it right, but as recommended in this Answer, I think this is possible in firebase.
router.beforeEach((to, from, next) => {
auth.onAuthStateChanged(async (userFirebase) => {
if (to.meta.requiresAuth) {
if (userFirebase) {
next();
} else {
await store.dispatch("auth/openLoginForm");
next("/");
}
} else next();
});
});

Related

Vue + Firebase route to prevent user back to login page after login

I develop SPA using VueJS 3 and firebase. I want prevent user from accessing login page after they login. But it seems the routes are keep looped or error after trying different code.
the routes:
{
path: '/', name: 'login', component: () => import('../views/LoginView.vue')
},
{
path: '/dashboard', name: 'dashboard', component: () => import('../views/DashboardView.vue'),
meta: {
requiresAuth: true,
},
},
the logic(didnt show anything):
const getCurrentUser = () => {
return new Promise((resolve, reject) => {
const removeListener = onAuthStateChanged(
getAuth(),
(user) => {
removeListener()
resolve(user)
},
reject
)
})
}
router.beforeEach(async (to, from, next) =>{
if (to.matched.some((record) => record.meta.requiresAuth)) {
if (await getCurrentUser()) {
next()
} else {
next("/")
}
} else {
next("/dashboard")
}
})
this one keep looping:
router.beforeEach(async (to, from, next) =>{
if (to.matched.some((record) => record.meta.requiresAuth)) {
if (await getCurrentUser()) {
next()
} else {
next("/")
}
}
next("/dashboard")
})
As mentionned in the Vue Router Doc :
You must call "next" exactly once in any given pass through a navigation guard. It can appear more than once, but only if the logical paths have no overlap, otherwise the hook will never be resolved or produce errors.:
They provide those examples :
// BAD
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
// if the user is not authenticated, `next` is called twice
next()
})
// GOOD
router.beforeEach((to, from, next) => {
if (to.name !== 'Login' && !isAuthenticated) next({ name: 'Login' })
else next()
})
Also, make sure you do not have a infinite loop :
next("/dashboad") forces a redirection to the Dashboard page, no matter what page we come from. You may end in a loop where every page redirects to Dashboard, including the Dashboard page itself

How to create a reactive statement in Svelte with Firebase's onSnapshot?

I'm learning Svelte for the past 4 days and I'm trying to integrate it with Firebase.
I need to listen to a document named after the user's uid after the user logged in which I saved in a writable name userStore.
Note: My background was React and this can be done easily with useEffect
I need a way to call unsubscribe in the onDestroy statement... How can I do that?
onDestroy(() => {
unsubscribe();
});
This is my current code:
$: if ($userStore)
onSnapshot(doc(db, 'userInfo', $userStore.uid), (doc) => {
if (doc.data()) {
console.log(doc.data());
userInfoStore.set(doc.data() as UserInfo);
}
});
I think onSnapshot() returns unsubscribe, so it should work like this
<script>
let unsubscribe
onDestroy(() => {
if(unsubscribe) unsubscribe();
});
$: if ($userStore) {
unsubscribe = onSnapshot(doc(db, 'userInfo', $userStore.uid), (doc) => {
if (doc.data()) {
console.log(doc.data());
userInfoStore.set(doc.data() as UserInfo);
}
});
}
</script>
Is the component destroyed when the user logs out? Because the unsubcription should be called if the user logs out? I think in a component might not be the best place to handle the logic. This would be a way via a custom store
userInfoStore.js
export const userInfoStore = (() => {
const initialValue = {}
const {subscribe, set} = writable(initialValue)
let unsubSnapshot
return {
subscribe,
startListener(uid) {
unsubSnapshot = onSnapshot(doc(db, 'userInfo', uid), (doc) => {
if (doc.data()) {
console.log(doc.data());
set(doc.data() as UserInfo);
}
});
},
stopListener() {
if(unsubSnapshot) unsubSnapshot()
}
}
})();
auth.js
onAuthStateChanged(auth, user => {
if(user) {
userStore.set(user)
userInfoStore.startListener(user.uid)
}else {
userInfoStore.stopListener()
}
})
App.svelte (main component)
Don't know how important that is or if the listener is stopped anyway when the page is closed
<script>
import {onDestroy} from 'svelte'
import {userInfoStore} './userInfoStore'
onDestroy(() => {
userInfoStore.stopListener()
});
</script>

Nuxt async not work on page reload - firebase

I have a issue with asyncData() when i refresh the page. If I navigate from list to single item, it work, but if i reload the page i will see an empty object.
In my page i have this :
<template>
<div>
{{ getItem}}
</div>
</template>
<script>
import { mapState } from 'vuex'
export default {
data: () => ({
}),
computed: {
...mapState([
'single_item'
]),
getItem() {
return this.single_item
}
},
async asyncData({app,route, params,store}) {
let type = 'posts'
let id = params.id
return store.dispatch('fetchFirebaseSingle', {type,id })
}
}
</script>
in store.js
import { db } from '~/plugins/firebase'
const actions = {
....
async fetchFirebaseSingle({commit}, {type, id}) {
try {
console.log('fetchFirebaseSingle', type)
const docRef = await db.collection(type).doc(id)
docRef.get()
.then((doc) => {
if (doc.exists) {
const file = doc.data()
commit('SET_PAGE_SINGLE', file)
} else {
// doc.data() will be undefined in this case
console.log("No such document!");
}
})
.catch((error) => {
console.log("Error getting document:", error);
});
} catch (e) {
console.log("Error getting document:", e);
}
},
}
const mutations = {
...
// Set Single Item
SET_PAGE_SINGLE ( state, single_item) {
state.single_item = single_item
},
},
const state = () => ({
single_item : {},
})
I tryed also to call directly from this page the database, but i have same issue. Did someone get similar issue with vuex and firebase or asyncData ?
Thanks
Nothing special here, asyncData is not supposed to work on page reload or a refesh (F5) but only with page transitions.
Unlike fetch, the promise returned by the asyncData hook is resolved during route transition
You could use the fetch() hook if you don't mind a non-blocking loading.
More info here: https://nuxtjs.org/docs/features/data-fetching#data-fetching

Protected Route With Firebase and Svelte

I'm trying to create a protected page, the profile page of my project. I want to throw people out if they are not logged in. I'm trying to do it as simply as possible. I find this tutorial, but is TypeScript and I couldn't get it to work. Link >
My way:
Profile page:
let auth = getAuth();
onMount(() => {
auth.onAuthStateChanged((user) => {
if (!user) {
goto('/signin');
}
});
});
The idea is to have a user store and use it with the combination of onAuthStateChanged
import authStore from '../stores/authStore';; // <~ stores.ts
import { onMount } from 'svelte';
let auth = getAuth();
onMount(() => {
//shift this method to a firebase.ts
auth.onAuthStateChanged((user) => {
if (user) {
authStore.set({
user,
});
} else {
authStore.set({
user: null,
});
}
});
});
// this block will watch changes on the store
$: {
if (!$authStore.user) {
if (browser) goto('/login');
} else {
if (browser) goto('/');
}
}

Nuxt middleware: How to access vuex store?

I am trying to block user on client-side from editing another user's profile. My URL structure is like so:
/users/edit/XpuBjKFoLSRHJAloNg38Amqn2jQ2
Thus, if user tries to acccess path of another user (ie, http://localhost:3000/users/edit/blahdasd) I need to redirect him to homepage.
I tried to set up an anonymous middle ware like so on my page:
export default {
middleware({ store, params, redirect }) {
if (store.state.user.currentUser.uid !== params.uid) {
return redirect('/')
}
},
But, I get page error of:
Cannot read property 'uid' of null
So, how do I correctly access the store here? I have no problem accessing uid from computed property on same page:
user() {
return this.$store.state.user.currentUser
},
Update (more information):
Here is my edit user profile page:
export default {
middleware({ store, params, redirect }) {
if (store.state.user.currentUser.uid !== params.uid) {
// return redirect('/')
console.log(store.state.user.currentUser.uid)
console.log(params.uid)
}
},
computed: {
user() {
return this.$store.state.user.currentUser
},
And here is my store/user.js file:
export const state = () => ({
currentUser: null,
})
export const mutations = {
SET_AUTH_USER(state, payload) {
state.currentUser = payload
}
}
export const actions = {
async onAuthStateChangedAction({ commit, dispatch }, { authUser }) {
console.log('auth state changed....')
try {
if (authUser && authUser.emailVerified) {
const {
uid,
email,
emailVerified,
displayName = '',
photoURL,
metadata,
providerData,
providerId,
tenantId
} = authUser
commit('SET_AUTH_USER', {
uid,
email,
emailVerified,
displayName,
photoURL,
metadata,
providerData,
providerId,
tenantId
})
console.log('fetching profile...')
await dispatch('getUserProfile', authUser)
} else {
console.log('User logged out or not verified')
return null
}
} catch (error) {
console.error('Error with Auth State observer: ', error)
}
},

Resources