Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Authentication using ReactJS and Firebase

I'm new to web development and started learning ReactJS.

Till now there is no problem with building simple web apps.

Now I wanted to use authentication using Firebase.

I know how to authenticate a user using Firebase but I'm not able to get how to design frontend to restrict access to certain pages.

Previously, I used PHP and in that I used session variable and include to include the parts of HTML to show to the user.

But I don't know how to do it in ReactJS using Firebase.

Initial and failed approach:

I thought of updating this.state.loggedIn and using that to show the component. But this is not a correct way because I'm sending both the components to display and easily we can change the state of a component using React developer extension in Chrome.

This is my main index.js code:

import React from 'react';
import ReactDOM from 'react-dom';
import Login from './components/Login/Login';
import Home from './components/Home/Home';
import fire from './components/Fire';


class App extends React.Component {

    constructor(props) {
        super(props);
        this.state = {
            loggedIn: false,
        };

        this.authListener = this.authListener.bind(this);

    }

    componentDidMount() {
        this.authListener();
    }

    authListener() {
        fire.auth().onAuthStateChanged(user => {
            if (user) {
              // User is signed in.
                console.log(user);
                this.setState({
                    loggedIn: true
                })
            } else {
              // No user is signed in.
                this.setState({
                    loggedIn: false
                });
            }
        });

    }

    render() {

        return (
            <div>
                {this.state.loggedIn ? <Home/> : <Login/>}
            </div>
        );
    }
}

ReactDOM.render(
    <App/>, document.getElementById('app'));

And In Login.js, I'm calling the Firebase authentication function.

Just the snippet:

fire
    .auth()
    .signInWithEmailAndPassword(this.state.email, this.state.password)
    .catch(function (error) {
          // Handle Errors here.
          var errorCode = error.code;
          var errorMessage = error.message;
          console.log(error);
          // ...
     });

The authentication is working fine and I can see it in the console log.

So, how should I do it in ReactJS + Firebase to authenticate?(Is it posible without using any other packages like react-routes etc?)

Or should I be using Cloud functions for this?

like image 294
ssh Avatar asked Nov 07 '22 05:11

ssh


1 Answers

UPDATE:

Everything you link in your html files should always come from your public directory. So, keep your bundled .js files in your public directory.

I have wrote some time saving tips for beginners. You can find more about that beginners tips here.


It is actually simple.

If you want to send only required html file to the browser, you have to hit the server every time you need a whole different html.

You said that you used PHP. In PHP, we use sessions to know whether a person is logged-in or not.

So, we try to implement it here.

First lets look at how sessions in PHP work. Taken from an SO Answer

In the general situation :

  • the session id is sent to the user when his session is created.
  • it is stored in a cookie (called, by default, PHPSESSID)
  • that cookie is sent by the browser to the server with each request
  • the server (PHP) uses that cookie, containing the session_id, to know which file corresponds to that user.

The data in the sessions files is the content of $_SESSION, serialized (ie, represented as a string -- with a function such as serialize) ; and is un-serialized when the file is loaded by PHP, to populate the $_SESSION array.


Sometimes, the session id is not stored in a cookie, but sent in URLs, too -- but that's quite rare, nowadays.


For more informations, you can take a look at the Session Handling section of the manual, that gives some useful informations.

For instance, there is a page about Passing the Session ID, which explains how the session id is passed from page to page, using a cookie, or in URLs -- and which configuration options affect this.

So, session is just a cookie. Then I guess we can use a cookie to find out about the user login status.

In Firebase, there is a problem with cookies. You can only set cookie name as __session. Other names are ignored by the Firebase and as a result you can't read cookies on the server.

You need to use Firebase Functions to do some backend work and serve html based on the user login status.

So, setup the Firebase Functions in your project directory. In the root,

$ firebase init functions

Now you have to say to Firebase that any requests coming to your domain must invoke a function.

To do that you have to write rewrites in your firebase.json file. Our function name will be main.

{
  "database": {
    "rules": "database.rules.json"
  },
  "hosting": {
    "public": "/dev",
    "ignore": [
      "firebase.json",
      "**/.*",
      "**/node_modules/**"
    ],
    "rewrites":[{
      "source": "**",
      "function": "main"
    }]
  }
}

Now create an index.js anywhere(Prefer root of the project directory as this is the main file).

index.js

import express from 'express';
import * as functions from 'firebase-functions';
import fs from 'fs';
var cookieParser = require('cookie-parser')

const app = express();
app.use(cookieParser());

app.get('**', (req, res) => {
    // Check the custom set cookie for user
    // If the user is not logged-in, redirect to Login. Otherwise, redirect to Home page
    if (req.cookies.__session == 'loggedin') {
        var index = fs.readFileSync(__dirname + '/src/Home/index.html', 'utf8');
    }
    else {
        var index = fs.readFileSync(__dirname + '/src/Login/index.html', 'utf8');
    }
    res.send(index);

});

export let main = functions.https.onRequest(app);

Some explanation about above parts:

  1. express: It is used to process the requests and send the response.

  2. firebase-functions: It is provided by the Google to use Firebase Functions.

  3. fs : We read appropriate html according to the user login status from the file system and serve it.

  4. cookie-parser: We use it to access our cookies easily.

Note: You need to convert it to ES5. So use babel command to convert it(I assume you saved index.js in your root.)

$ babel index.js -d functions

We are using cookie named __session to know about the user login status.

If the __session is set to loggedin, we send the user Home html. Otherwise, we send Login html.

Now, the real question is: "Where do we need to set the cookie __session?"

We set the cookie in the user browser.

We make use of onAuthStateChanged().

Call this method in your Login page component like:

componentDidMount() {
     this.authListener();
}


authListener() {
        fire.auth().onAuthStateChanged(user => {
            if (user) {
                // User is signed in.
                if (Cookies.get('__session') === undefined) {
                    Cookies.set('__session', 'loggedin', { expires: 3652 });
                    window.location.reload();
                }
            } else {
              // No user is signed in.
              Cookies.remove('__session');
            }
        });
    }

Note: Cookies object is imported from js-cookie. So, install it.

What we are doing here is:

  1. First the onAuthStateChanged() is called initially when the page is loaded.

  2. If the user is already logged-in, we enter into if block.

  3. Then we check for the __session cookie. This is important because sometimes user can clear all the cookies. When we try to read the cookie in the server, we get undefined and we send the login page to the user. Process is repeated from 1.

  4. Then we reload the page. When we reload the page, the user hits the server again. Then we can check for the __session cookie and send the Home html to the user.

like image 72
SkrewEverything Avatar answered Nov 09 '22 22:11

SkrewEverything