Reactive Firebase Auth with Vue
Firebase offers great service for authenticating users in your app. It’s for those of you who -like me- don’t want to spend long hours, first choosing and learning backend framework and then implementing authentication.
There’s a great article about firebase auth in Vue apps. If this is your first encounter with VueJS+Firebase you should definitely check it out. Anas Mammeri does a great job explaining from scratch how to get started.
The approach was easy and straightforward but when you’re build an app you’ll probably face same problems as I did.
The problem
While playing around I struggled with following concerns:
- how to update components that depend on user auth state when it changes
I really wanted to take full advantage of firebase reactivity and respond to changes on user model rather than just check the model when I needed to. Suppose we have navigation menu that depends on user state. If user is authenticated, we show avatar and logout button, otherwise just show sign-in/sign-up links. We also want the component to update anytime user state changes (e. g. sign in, logout )
- separate Firebase Auth logic from the app
I really don’t feel comfortable referencing firebase lib (or any other for that matter) directly in the components (or actions) and they should now about the auth as little as possible.
- require login before entering particular route, and continue to it after user signs
Theres nothing more frustrating when entering a specific page than first having to sign in and then being redirected to main page again.
The solution
All the code can be found here
- Move any firebase logic into custom Vue plugin
We’ll have all our Firebase code wrapped around a plugin and the app will interact with the service only through plugin methods.
- Store user object in Vuex
We will keep our user stored in Vuex store so we can take advantage of all goodies that come with Vue+Vuex( getters
, mapGetters
, computed
properties etc.) and keep data flow in our app as simple and robust as possible.
- pass in params next route and redirect after login
In order to continue to previous route after sign in, we’ll save our route in query params and pass them tho sign in component (e. g. /signin?redirect=%2Fprofile
). Then, after login the app will replace /signin
route with target route (e. g. /profile
)
Vuex setup
First let’s setup our store to handle user accounts.
If your not new to Vuex there’s nothing new here:
- setup initial user object to be
null
(not logged int)line 7
- add mutation for handling user updates
line 11
- add getter for observing user
line 16
Firebase-auth plugin
Now, when we have our store ready, let’s create a plugin wrapper for firebase auth service
- set up a new plugin
FirebaseAuthPlugin
(see here for details) config
contains standard Firebase credentials (you can get them here), please remember to set up authentication- import current app
store
$auth
object will contain all auth operations (login, logout, registration)- observe
auth
state changes inonAuthStateChanged
and dispatch any changes to ourstore
- note that
login
andlogout
areasync
because we also want to know if the request completed
Update: handling auth session
Following Daniel da Rocha question — all auth session management is handled by firebase-auth plugin, so all you need to do is to ensure that your user
model in the store is synced with model returned in onAuthStateChanged
Continue back to route after user signs in
Now, lets setup our router
Our router has pretty much standard configuration. Thing to note is meta
field in /profile
route. This will indicate that particular route is restricted only for logged in users.
Note that we import our store
in order to have access to user
object.
The whole magic happens in beforeEach
method. If you already did some Vue development it’s probably nothing new otherwise read about vue-router. First we check if next ( to
) has authReqired
flag. If yes, then check wether the user is signed in. If no, we redirect to /signin
and add our target route as redirect
Result
So now that we have our auth bound with Vuex store
we have a normal data flow so in our Navigation
component we can look like this:
Note that as bonus we get a bit less complicated test cases as we don’t worry about mocking Firebase, instead just mock our Vuex store with data needed in the component and make assertions.
Update : Keeping user sign in after page reload
Another good question from Daniel da Rocha — will the user have to re-authenticate after closing the browser or reloading the page?
We should consider two scenarios here.
- User enters a route which does not require authenticated user.
In this case we don’t have to worry about re-authenticating the user as firebase will consist our session. After reloading the page onAuthStateChange
will be re-attached and if the session will return user
then we’ll just commit it to our store and trigger any updates in our components.
2. User enters a route which does require authenticated user.
This is more complex because our router usually gets called before onAuthStateChange
is triggered, so we probably won’t have user
in our store.
This can be easily fixed by attaching watcher in our sign in component:
This ensures that anytime user model changes and is a valid user we will be redirected to our destination route.
Unfortunately in most cases, for a brief second users will see sign in screen — that’s a bit awkward from UX point of view. But we can go a step further and ensure the user
is cached. To do this we could use Vuex plugins to store our user
in localStorage
. Please read documentation for more info.
Summary
By moving Firebase boilerplate into Vue plugin we now have all the auth related logic in one place. We also made our user data reactive so no anytime our user model changes, the app and components are also updated. Also unit testing should be a bit simpler since we don’t have to stub Firebase service in order to render component.
Check out the code here