With Vue Router you might want to guard some paths and redirect users to a signup page if they are not authenticated. I will show you how this is done correctly with Firebase Authentication.
This article assumes you already have implemented your project with Vue Router and Firebase Authentication
First, we must mark each route that we want to guard with a meta property called requiresAuth
.
const routes = [
{
path: '/signin',
component: SignIn,
},
{
path: '/profile',
component: Profile,
meta: {
requiresAuth: true,
},
},
]
In this example, the path /signin
is allowed for everyone, but /profile
should only be for signed-in users.
Now we can use the beforeEach
guard to check for this property.
router.beforeEach((to) => {
const requiresAuth = to.matched.some((record) => record.meta.requiresAuth)
if (requiresAuth && !currentUser) {
return '/signin'
}
})
Now if the currentUser
is null
or undefined
, we should redirect users to the sign-in path. But how do we get currentUser
? We can’t use getAuth().currentUser
because on page refresh that property has not been set yet before the guard is triggered. We will have to use the onAuthStateChanged
callback somehow. Let’s create a function that waits for the auth state to be set.
const app = initializeApp(firebaseConfig)
const auth = getAuth(app)
function getCurrentUser() {
return new Promise((resolve, reject) => {
const unsubscribe = onAuthStateChanged(
auth,
(user) => {
unsubscribe()
resolve(user)
},
reject
)
})
}
This method will return a Promise which resolves currentUser
as soon as it is set. onAuthStateChanged
will trigger the callback immediately with either null or the user object if signed in. Then we unsubscribe to not listen for further changes.
Now we will update our beforeEach
guard so that only paths that require authentication await this method.
router.beforeEach(async (to) => {
const requiresAuth = to.matched.some((record) => record.meta.requiresAuth)
if (requiresAuth && !(await getCurrentUser())) {
return '/signin'
}
})
That’s all. This also simplifies getting the currentUser for components under the guarded routes, because we know getAuth().currentUser
is set.
For full example check out this GitHub repository and demo at vue-routes-authentication.web.app
Top comments (10)
Hello! it's pretty well, if you are in firebase 9 is the same, but only you have to create a new function without call "firebase.getCurrentUser" and importing like this:
import { onAuthStateChanged } from 'firebase/auth'
import { auth } from 'src/firebase'
const user = () => {
return new Promise((resolve, reject) => {
const unsubscribe = onAuthStateChanged(auth, (userFirebase) => {
unsubscribe()
resolve(userFirebase)
}, reject)
})
}
and later when you going to call it in a function has to be async obviusly like:
const data = await user()
thanks bro!
This is awesome! This is a major issue (I ended up thinking I will have to write my entire app in a single file to avoid that auth problem) and I am surprised I haven't found an elegant solution like that anywhere else on the web!
The next problem, I'm trying to fight it on github.com/tarkhil/authts is that RouterGuard does nothing on the first request to the router.
I have no idea what I could make wrong. But now it's 100% repeatable for me
Seems to be the most complete and elaborated example on the subject
Thank you! You've made my day!
Unfortunately, firebase.getCurrentUser = () requires something additional in TypeScript and I did not get answer yet
Usually, TS won't let you add a method to another object without something additional.
But maybe you can just assign the method to a new variable instead:
I can confirm that this approach worked for me in TS. :-) Many thanks @gautemeekolsen for sharing this knowledge.
That just saved me from probably several hours of trying to figure out how to solve it, and I still wouldn't have done it as elegantly as that. :-) Thanks!
Me ajudou muito... algo simples e direto!!!
Parabéns!!!