DEV Community

Maksim Ivanov
Maksim Ivanov

Posted on • Originally published at maksimivanov.com on

Firebase React Authentication Tutorial

Sometimes you just need to make a fast prototype and you don’t want to mess with back end, authentication, authorization and all of that. Here is where Google’s firebase can help you. In this tutorial I’ll show you how to connect your react app with Firebase authentication module.

What Are We Going To Build

Super simple app. Just 3 screens: Sign up, Log in and Home screen.

We’ll use Firebase Authentication module to handle login/sign up and React router to manage routing.

Make sure to read till the end, I’ll post a link to Github repo with example code there.

Set Up Firebase

Create New Firebase App

First go to firebase console and create new app.

New firebase app

Add Auth Method

Click Authentication and then SET UP SIGN-IN METHOD.

New sign in method

Enable Email/Password authentication.

Get Firebase Credentials

Go to project settings:

New sign in method

And select Add firebase to your web app. Copy your credentials from there and save them to .env file in your project’s root.

REACT_APP_FIREBASE_KEY=your_key
REACT_APP_FIREBASE_DOMAIN=your_app_id.firebaseapp.com
REACT_APP_FIREBASE_DATABASE=https://your_app_id.firebaseio.com
REACT_APP_FIREBASE_PROJECT_ID=your_app_id
REACT_APP_FIREBASE_STORAGE_BUCKET=your_storage_bucket
REACT_APP_FIREBASE_SENDER_ID=sender_id
Enter fullscreen mode Exit fullscreen mode

create-react-app webpack config automatically loads environment variables that start with REACT_APP, so we can reference them.

Read more about it in create-react-app documentation

Set Up The Front End

We will set up the application using create-react-app. I assume that you have modern version of npm so I’m going to use npx to run the script.

npx create-react-app firebase-auth-tutorial
Enter fullscreen mode Exit fullscreen mode

Also we’ll need routing so install React Router as well:

yarn add react-router
Enter fullscreen mode Exit fullscreen mode

Also remove src/index.css, src/App.css and src/App.test.js files. We won’t need them now.

Connect App To Firebase

First install firebase package:

yarn add firebase
Enter fullscreen mode Exit fullscreen mode

Now create file src/base.js with following contents:

import firebase from "firebase";

const app = firebase.initializeApp({
  apiKey: process.env.REACT_APP_FIREBASE_KEY,
  authDomain: process.env.REACT_APP_FIREBASE_DOMAIN,
  databaseURL: process.env.REACT_APP_FIREBASE_DATABASE,
  projectId: process.env.REACT_APP_FIREBASE_PROJECT_ID,
  storageBucket: process.env.REACT_APP_FIREBASE_STORAGE_BUCKET,
  messagingSenderId: process.env.REACT_APP_FIREBASE_SENDER_ID
});

export default app;
Enter fullscreen mode Exit fullscreen mode

Add Routing

Open src/App.js and make it look like this:

import React from "react";
import { BrowserRouter as Router, Route } from "react-router-dom";

import Home from "./Home";
import Login from "./Login";
import SignUp from "./SignUp";

const App = () => {
  return (
    <Router>
      <div>
        <Route exact path="/" component={Home} />
        <Route exact path="/login" component={Login} />
        <Route exact path="/signup" component={SignUp} />
      </div>
    </Router>
  );
};

export default App;
Enter fullscreen mode Exit fullscreen mode

Create Home, LogIn and SignUp components, for now just render some header. Here, for instance src/Home.js:

import React from "react";

const Home = () => {
  return <h1>Home</h1>;
};

export default Home;
Enter fullscreen mode Exit fullscreen mode

Run the application. You should have all routes available.

Create The Sign Up And LogIn Components

Now let’s add some sign up logic. Create the src/SignUp directory and move our SignUp.js there. Also rename it to SignUpView.js. Make it look like this:

src/SignUp/SignUpView.js

import React from "react";

const SignUpView = ({ onSubmit }) => {
  return (
    <div>
      <h1>Sign up</h1>
      <form onSubmit={onSubmit}>
        <label>
          Email
          <input
            name="email"
            type="email"
            placeholder="Email"
          />
        </label>
        <label>
          Password
          <input
            name="password"
            type="password"
            placeholder="Password"
          />
        </label>
        <button type="submit">Sign Up</button>
      </form>
    </div>
  );
};

export default SignUpView;
Enter fullscreen mode Exit fullscreen mode

This is simple presentational component. We get the onSubmit handler as one of the props and attach it to our form. We defined email and password fields and added Sign Up button.

Now create the container component src/SignUp/index.js:

import React, { Component } from "react";
import { withRouter } from "react-router";
import app from "../base";

import SignUpView from "./SignUpView";

class SignUpContainer extends Component {
  handleSignUp = async event => {
    event.preventDefault();
    const { email, password } = event.target.elements;
    try {
      const user = await app
        .auth()
        .createUserWithEmailAndPassword(email.value, password.value);
      this.props.history.push("/");
    } catch (error) {
      alert(error);
    }
  };

  render() {
    return <SignUpView onSubmit={this.handleSignUp} />;
  }
}

export default withRouter(SignUpContainer);
Enter fullscreen mode Exit fullscreen mode

This component will handle our sign up logic.

Let’s look at our handleSignUp function. It’s defined as an anonymous arrow function. I did it here to avoid using bind(this).

In this example I really need class level this because of the history object I get from props by using withRouter wrapper. Otherwize I’d better define it as a regular function.

So in this function I preventDefault to avoid reloading page, get password and email from form elements and try to create new user on Firebase using createUserWithEmailAndPassword function.

Our LogIn component will be almost the same, just change the createUserWithEmailAndPassword function call to signInWithEmailAndPassword

Add Private Routes

Ok, great now we can sign up, and log in, but unfortunately it doesn’t make much sense, because home page is available even for the non-authorized user. Let’s fix it.

Create src/PrivateRoute.js with following contents:

import React from "react";
import { Route, Redirect } from "react-router-dom";

export default function PrivateRoute({
  component: Component,
  authenticated,
  ...rest
}) {
  return (
    <Route
      {...rest}
      render={props =>
        authenticated === true ? (
          <Component {...props} {...rest} />
        ) : (
          <Redirect to="/login" />
        )
      }
    />
  );
}
Enter fullscreen mode Exit fullscreen mode

Basically this component will conditionally render either passed Component or <Redirect/> block, depending on passed authenticated prop.

Now let’s use it in our App.js. Change the Home route to PrivateRoute:

<PrivateRoute exact path="/" component={Home} authenticated={this.state.authenticated}/>
Enter fullscreen mode Exit fullscreen mode

We used authenticated field of our state, but it doesn’t exist yet. Let’s fix it.

Monitoring Auth Status

First remake your App.js to normal Component and set initial state:

class App extends Component {
  state = { loading: true, authenticated: false, user: null };

  render() {
    const { authenticated, loading } = this.state;

    if (loading) {
      return <p>Loading..</p>;
    }

    return (
      <Router>
        <div>
          <PrivateRoute
            exact
            path="/"
            component={Home}
            authenticated={authenticated}
          />
          <Route exact path="/login" component={LogIn} />
          <Route exact path="/signup" component={SignUp} />
        </div>
      </Router>
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Now add componentWillMount to you App.js with following cotents:

componentWillMount() {
  app.auth().onAuthStateChanged(user => {
    if (user) {
      this.setState({
        authenticated: true,
        currentUser: user,
        loading: false
      });
    } else {
      this.setState({
        authenticated: false,
        currentUser: null,
        loading: false
      });
    }
  });
}
Enter fullscreen mode Exit fullscreen mode

So now we’ll render Loading... until we get data from Firebase and update our state. Then we render routing and PrivateRoute redirects us to Log In page if we are not signed up.

What To Do Next

Now you can add a redirect from login page if you are logged in already, also you can add log out functionality (go read firebase documentation)

Also with this knowledge you can add authentication to wallet app from the React Ethereum Tutorial!

Oh, BTW – here is your Github link

Top comments (7)

Collapse
 
Sloan, the sloth mascot
Comment deleted
Collapse
 
satansdeer profile image
Maksim Ivanov

Hey Duane, what do you mean "data is stored in network"?

Would he easier to help if youd show an example repo.

Collapse
 
pretaporter profile image
Maksim

Hello there! I don't know a lot about firebase. Is i right understand, that credentials for db are accessed on client side code?

Collapse
 
dbanisimov profile image
Denis Anisimov

You're right, API key is public. But there is nothing wrong with it, as in Firebase world the client-side API key is merely an identifier your apps use to talk to Firebase services and the real authorization happens with the help of Firebase Auth + Security rules.

Once the user logs in on the client a short-lived JWT token is issued by Firebase Auth, that token is passed with every request to RTDB, Firestore or Storage and Security Rules are used to authorize or not the action.

You can read more here RTDB and Firestore

Collapse
 
satansdeer profile image
Maksim Ivanov

If I understood you correctly - yes. But it's important to note that you shouldn't commit those to git.

Collapse
 
nano1406 profile image
Nano1406

Hi, first of all...great post!! I have one problem...when I refresh the app from a private route the router redirect me to the login page everytime. This happend because when the app renders for the first time, the user isn't authenticated so the only thing to do next is redirect to "/login" but then when the status of the user change to "authenticated" nothing more happend and the app keep in the logine route. I don't know if I miss something or there is something missing in the post...

Collapse
 
petecapecod profile image
Peter Cruckshank

Thanks this was great. Just what I was looking for 👍
Now I'm going to try to rewrite it with Hooks 😎