Authentication with Firebase in NextJS, with SSR!
Tags: webdev, firebase, nextjs, typescript
Published: 2023-01-20 11:40:06


## Introduction

In this post, we will learn how to set up user authentication in a Next.js application using Firebase. Firebase is a popular backend-as-a-service provider that offers a variety of tools for building web and mobile applications, including user authentication. By the end of this tutorial, you will have a basic understanding of how to use Firebase to authenticate users in your Next.js application, allowing them to sign in, sign up, and sign out.

## Getting Started

The first step will create a project under [firebase console](https://console.firebase.google.com), once you create a web project, firebase will provide you a code snippet to use on the client side.

```jsx
// Import the functions you need from the SDKs you need
import { initializeApp } from 'firebase/app';
import { getAuth } from 'firebase/auth';
import { getFirestore } from 'firebase/firestore';

// TODO: Add SDKs for Firebase products that you want to use
// https://firebase.google.com/docs/web/setup#available-libraries

// Your web app's Firebase configuration
// For Firebase JS SDK v7.20.0 and later, measurementId is optional

const firebaseConfig = {
 apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY,
 authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN,
 projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID,
 storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET,
 messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID,
 appId: process.env.NEXT_PUBLIC_FIREBASE_APP_ID,
 measurementId: process.env.NEXT_PUBLIC_FIREBASE_MEASUREMENT_ID,
};


// Initialize Firebase
const app = initializeApp(firebaseConfig);
export const db = getFirestore(app);
export const auth = getAuth(app);

```

## Client Side Authentication 

Once you initialize the firebase app, you are good to go!

Firebase has a lot of providers for authentication, like social logins with Google, Github, Apple, Facebook and etc. Also, you can choose a simple email and password login. In this example, I’ll create the users manually, since only admin users will be able to log in.

After selecting the provider and creating the users, we can start building our sign-in logic.

```jsx

 import { auth } from 'configs/firebase-config';
 import { onIdTokenChanged, User } from 'firebase/auth';

 const [admin, setAdmin] = useState<User | null>(null);

 useEffect(() => {
   const unsubscribe = onIdTokenChanged(auth, async (user) => {
     if (user) {
       setAdmin(user);
     } else {
       setAdmin(null);
     }
   });

   return unsubscribe;
 }, []);
```

So, we will use this useEffect hook on the login page, if someone logs in, it automatically assigns the user to our state. And this is the actual login function:

```jsx
import { signInWithEmailAndPassword } from 'firebase/auth';

 const [email, setEmail] = useState('');
 const [password, setPassword] = useState('');

 const signInwithFirebase = async () => {
   try {
     await signInWithEmailAndPassword(auth, email, password);
   } catch (error: FirebaseError | unknown) {
     if (error instanceof FirebaseError) {
        console.log(code);
     }
   }
 };

```

That’s it! Now you are authenticated with Firebase Auth. After this point, you can create a Context Provider to store the Auth User in the whole application.

There is one caveat in this approach and that is SSR. Since we are checking the authentication on the client side, when we first load the page, we might get some flickery behaviors. But wait, there is a solution for this also! 

## Server Side Authentication - Power of the “getServerSideProps”

So, the basic idea is that we need to validate the authenticated user on the backend server before loading the page. Able to do this, we need another library called firebase-admin, with this library we are able to do a lot of things such as creating users, databases, and validations.

Here is the basic configuration of the firebase-admin app:

```jsx

import * as admin from 'firebase-admin';

/* eslint-disable import/prefer-default-export */
import { credential } from 'firebase-admin';
import { initializeApp } from 'firebase-admin/app';

const serviceAccount = require('ServiceAccountFile.json');

if (!admin.apps.length) {
 initializeApp({
   credential: credential.cert(serviceAccount),
 });
}

export const adminSDK = admin;

```

Once we configured the adminSDK, we can use it on our application. 

So, the logic of the server side auth verification is that we need to validate the users token on the backend. So, once we are logged in as a user, firebase gives us a unique user token. Somehow we need to pass this token value to our backend and the best way to do this cookies! 

Here is the implementation of the server side validation of the authenticated user:

```jsx
import nookies from 'nookies';
import { adminSDK } from 'configs/firebase-admin-config';

export async function getServerSideProps(ctx: GetServerSidePropsContext) {
 const cookies = nookies.get(ctx);
 if (!cookies.token) {
   return {
     props: {
       isLoggedIn: false,
     },
   };
 }

 try {
   const token = await adminSDK.auth().verifyIdToken(cookies.token);
   if (!token) {
     return {
       props: {
       	isLoggedIn: false,
       },
     };
   }

   // the user is authenticated!
   const { uid } = token;
   const user = await adminSDK.auth().getUser(uid);

   return {
     props: {
       isLoggedIn: true,
     },
   };
 } catch (error) {
   return {
     props: {
       isLoggedIn: false,
     },
   };
 }
}

```


And of course, once we logged on the client side we need to store the user token in the cookies. So, the client side implementation will be change a little bit.

```jsx
useEffect(() => {
   const unsubscribe = onIdTokenChanged(auth, async (user) => {
     if (user) {
       setAdmin(user);
       const token = await user.getIdToken();
       nookies.set(undefined, 'token', token, { path: '/' });
     } else {
       setAdmin(null);
       nookies.set(undefined, 'token', '', { path: '/' });
     }
   });

   return unsubscribe;
 }, []);

```

## Conclusion

That’s it! Now, you have both client and server-side authentication. Using this approach you can create excellent web applications.
We have learned how to set up user authentication in a Next.js application using Firebase. We have seen how Firebase makes it easy to handle user registration and login, and how to use Firebase's authentication API to authenticate users in our application. We also saw how to use Firebase's Firestore to store user data and how to protect routes based on the authenticated user. With this knowledge, you should now be able to add user authentication to your Next.js applications easily and securely. Happy hacking!