But…why session cookies?
Heard about Firebase Auth and it’s cool features that handle all the dirty Auth flow for you? Awesome, right?
Well, yes and no. Firebase Auth has been mainly used on the client side. Convenience but at the cost of performance. The client has to make numerous calls to the server, exchanging tokens and what not for every page that needs a user to be signed in. Back in the day when I didn’t know React, my endeavours to hack up a vanilla JS app led to a conclusion that this was not a good fit. We can do better.
Behold session cookies. Embedded in requests, these little guys are sent to the server and user information can be extracted from them by our server machine — yeah, no shortage of brutal computing power there.
Effectively, it’s one request with a cookie, checked on server and content rendered accordingly. No unnecessary back and forth. Sweet!
Having said that, implementing Firebase session cookies can be a pain due Firebase’s rather underwhelming documentation. This guide seeks to help you speed up that process filling the blanks where needed.
Installing Node.js and Firebase CLI
Node.js ships with npm and is needed to install Firebase CLI and a few packages later on. Firebase CLI is used for initialising projects, deployment etc.
- Install Node.js
- Install the Firebase CLI using npm.
$ sudo npm install -g firebase-tools
Creating a Firebase Project and Initialisation
- In the Firebase console, create a new project giving it an appropriate name.
- Login into your google account with Firebase CLI from the terminal. This will open your browser for authentication.
$ firebase login
$ firebase init
A project structure as follows will be created after the above command. The public folder is used for static assets like CSS, JS etc.
Setting up Handlebars, Express & cookie-parser
Handlebars is a templating engine used to render dynamic pages. Although the use of dynamic content in this guide is limited, we use it to render sample static pages to the client. Yes, public assests can be used to access static pages from a server, but the aim here is to check if a user is signed in and render accordingly which is not possible if we serve a page statically. Having said that, we will still use static assets like JS, CSS etc., which are publicly accessible, in pages rendered using handlebars.
- In the terminal, navigate to the functions directory and run the following to install handlebars.
$ npm install --save express-handlebars
- Create a directory named views in the functions directory. This will contain the .hbs files that will contain our sample pages.
Express will be used to define behaviour of routes, check for signed-in, and render pages accordingly.
- Install Express in the same functions directory.
$ npm install --save express
We will be using cookies, hence the cookie-parser module can help handing the dirty work of extracting cookies from requests. Install it in the same directory as well.
$ npm install --save cookie-parser
Enabling Google Sign-in
We will only be using Google sign-in in this guide.
- From the Firebase console, go to your project
- Select Authentication from the left panel
- Under the sign-in methods tab, enable Google
That’s it for the setup, let’s get to the real stuff.
Setting Up the cloud function to use Express
- Modify the /functions/index.js file so that it looks like this.
The above is a starter code that creates five routes and also initializes cookie-parser, Firebase-admin and the handlebar templating engine. The following functionality needs to be implemented on the routes.
- / … this is the root route i.e when you do not enter any route in the hosted URL
- /newPage …this is a route that will display the user UID and will only load if a user is signed in else it will redirect to /signin
- /signin …route that performs user signin
- /signout …route that performs user signout
- /sessionLogin …route that receives an idToken from the client and generates and saves a session cookie in the response
Line 50 in the above code sets our Firebase Function name to app and instructs it to use the express app for all requests made to the function.
- Modify the firebase.json file in the root directory. This rewrites all requests from from any source path to our app Firebase Function.
- Create signin.hbs in /function/views
This is the view that will be rendered to the user. It uses FirebaseUI components to generate a sign in flow. As can be seen in Line 14, it uses a static script. This script initializes the FirebaseUI components and defines behaviour on user sign in. Create signin.js in /public/scripts
Line 2–9: Enter your Firebase project credentials here
- Open your project fromFirebase console
- In the project overview select +add app
- Select web and add the credentials in signin.js
Line 16 defines a uiConfig object that configures the FirebaseUI that is rendered to the user. We only use Google sign-in in our sign-in options. We also define a signInSuccess callback that allows us to extract the idToken of the signed-in user and send it to the cloud function to generate a session cookie.
We use session cookies to keep track of whether a user is signed-in. Firebase does also offer client side alternatives for session tracking but session cookies are generally much faster.
Line 33 makes a call to the /sessionLogin route with idToken as a query parameter of the GET request. We will define this route shortly. This route generates and saves a cookie in the response.
- Finally, define the /signin route in index.js
As described earlier, this route handles generation of session cookies. We will define a function setCookie() that takes an idToken and response object as parameters.
- Add the following function in index.js
When the above function redirects to /newPage the cookies are set on the request and can be used to extract user information. More on this in the next section.
This route only loads if a user is signed-in i.e only when valid cookies are set in the request. The page rendered displays the UID of the current user. Here is what it looks like
Achieving this is a two step process.
- Check if cookie is valid and add an object containing the UID to the incoming request object that is to be processed by the route. This is done using a middleware function.
- Add the following middleware function to index.js
Line 8 adds a decodedClaims object(which contains the UID) to the request object and next() invokes the callback defined on the /newPage route.
- Now let’s define the /newPage route in index.js
This adds the middleware function in the route and also extracts the UID from the decodedClaims object that was added on the request object in the previous step. Line 4 renders newPage.hbs passing to it the UID.
- Create /public/newPage.hbs as follows
Line 10 uses the uid passed from the previous step.
/ route (root route)
This is a the home route that offers options to sign-in, sign-out and a link to /newPage to test cookie based session tracking. Here is what it looks like
- define the route in index.js
- add home.hbs in /public
This routes clears the set cookies hence effectively signing out the user and redirects to the root route.
- Define the route as follows
That’s it! You can now deploy and test this using the following command in your project folder. A URL to the hosted app will be presented when the command finishes execution. Use it to play around with what you just created.
$ firebase deploy
- Firebase only supports setting of one cookie and with a specific name __session
- The signout route needs to have a redirect to clear cookies as desired. Using render(), send() etc. doesn't work