Firebase and Authentication

Monday, June 19, 2017

Lecture Videos

Morning:

Afternoon:

Topics

Firebase

  • Getting started
  • Database rules
  • Re-base for syncing React state with Firebase

Firebase Authentication

  • ‘firebase/auth’
  • authWithPopup
  • signing in and out
  • handling auth state changes

Deployment

  • GitHub Pages

Firebase

Getting Started

Firebase is a real-time database hosted by Google. In addition to the database, it also provides features of authentication, analytics, cloud storage, and hosting. For Noteherder, we synced the state of our app to our database on Firebase. This allowed all of our data to be persisted, even after page refreshes.

Re-base is an open source package that allows easy syncing of local state with a Firebase database. Add rebase to your project with one of the following commands:

user@localhost ~
 
yarn add re-base               # add package using yarn
npm install re-base            # add package using npm

Once you have re-base installed, setup is easy! First, create a new project on Firebase, then click on “Add to a web app” to see your JavaScript config object. Next, initialize a Firebase app and database in your project using the config object, and provide the database to re-base.



import Rebase from 're-base'
import firebase from 'firebase/app'
import database from 'firebase/database'

const app = firebase.initializeApp({
  apiKey: "YOURAPIKEY",
  authDomain: "YOURAUTHDOMAIN",
  databaseURL: "YOURDATABASEURL",
  projectId: "YOURPROJECTID",
  storageBucket: "YOURSTORAGEBUCKET",
  messagingSenderId: "YOURSENDERID"
})

const db = database(app)
const base = Rebase.createClass(db)

export default base


Finally, call base.syncState to sync your app’s local state with Firebase. The first argument to syncState is the name of the Firebase endpoint you want to sync, and the second is a configuration object.



base.syncState('myFavoriteEndpoint', {
  context: this,
  state: 'items'
})


Now, any time we update the state of our app, the changes will sync with Firebase in real time.

More Re-base Options

Re-base can do much more than just syncing state. There are methods for fetch, push, post, etc. To find out more about what all you can do with re-base, check out the README

Rules

For your Firebase database, you can set up rules (written in JSON) that specify the conditions under which data is allowed to be read or written. By default, a newly generated project will require that a user be authenticated to read or write any data.



{
  "rules": {
    ".read": "auth != null",
    ".write": "auth != null"
  }
}


If you do not have authentication set up yet, these values can be set to true. This allows anyone to read or write any data in the database. This can be convenient, but probably not a good idea long-term (and you will get a warning if you do that).

Additional rules can be applied per endpoint:



{
  "rules": {
    "emails": {
      ".read": true,
      ".write": "auth != null"
    },
    "texts": {
      ".read": true,
      ".write": "auth != null"
    },
    "users": {
      "$userId": {
        ".read": "auth != null && auth.uid == $userId",
        ".write": "auth != null && auth.uid == $userId"
      }
    }
  }
}


The above rules translate to:

  • texts and emails can be read by anyone, but only written by authenticated users
  • users data can be read and written only by an authenticated user whose uid matches the $userId of that item

Authentication

Firebase isn’t just a real-time database. It can also provide authentication services via email/password, phone, or common third-party services like Github, Facebook, and Google. For Noteherder, we set up authentication via Github OAuth.

Step 1: Get your authorization callback URL from Firebase

Navigate to your project in Firebase console. Click on the ‘Authenticate’ tab on the left and then on the Github logo. Copy the authorization callback URL.

The data for the Client ID and Client Secret will be generated in the next step.

Step 2: Register your app in Github

Log in to Github and click on ‘Settings’. On the left hand side, click on ‘OAuth Applications’ under the ‘Developer settings’ menu. Register a new app and fill out the form.

Use the Authorization callback URL from step 1

After successfully registering the app, you’ll be taken to your new app’s settings page.

Seeeeecrets... (Don't worry, this app has been deleted. Never post your app secrets publicly.)

Step 3: Enable Github authentication in Firebase

Go back to the Github authentication tab in Firebase and fill in the Client ID and Client Secret that you got from registering your app with Github.

More Seeeeecrets... (But seriously, don't share your secrets)

Step 4: Add Firebase auth to your app

Note: This step assumes you already have your Firebase database added to your app.

Import firebase/auth into your app’s firebase setup. Enable firebase auth and also create an instance of GithubAuthProvider.

base.js


  
import Rebase from 're-base'
import firebase from 'firebase/app'
import database from 'firebase/database'
import 'firebase/auth'

const app = firebase.initializeApp({
  apiKey: "YOURAPIKEY",
  authDomain: "YOURAUTHDOMAIN",
  databaseURL: "YOURDATABASEURL",
  projectId: "YOURPROJECTID",
  storageBucket: "YOURSTORAGEBUCKET",
  messagingSenderId: "YOURSENDERID"
})

const db = database(app)

export const auth = app.auth()
export const githubProvider = new firebase.auth.GithubAuthProvider()

export default Rebase.createClass(db)

  
  

Step 5: Set up the SignIn Component

Import auth and the githubProvider into whatever component handles the sign-in process. Call signInWithPopup on the auth object, passing the provider as a parameter. This will launch a popup screen that will prompt the user to sign in using the provider you have specified.

SignIn.js



import React from 'react'
import { auth, githubProvider } from './base'

const SignIn = () => {
  const authenticate = (provider) => {
    auth.signInWithPopup(provider)
  }

  return (
    <button className="SignIn" onClick={() => authenticate(githubProvider)}>
      Sign In With GitHub
    </button>
  )
}

export default SignIn


Step 6: Handling auth state changes (and page refreshes)

Once the user has authenticated via the popup, the state of our authorization has changed (we now have an authenticated user). Other events that can cause auth state changes are signing out, timeouts, and page refreshes. We should probably set up something to listen for these events. In the componentWillMount lifecycle hook that runs when the Component is first getting loaded, we can call the onAuthStateChanged method provided on the global auth object to set up such a listener.

App.js



// ...

componentWillMount() {
  auth.onAuthStateChanged(
    (user) => {
      if (user) {
        // finish signing in
        this.authHandler(user)
      } else {
        // finished signing out
        this.setState({ uid: null })
      }
    }
  )
}

// ...


Step 7: Finishing sign-in

What the authHandler callback does is up to you, but for Noteherder, we had it do pretty typical things - save the user ID to state, and initialize syncing our local state for ‘notes’ with the data stored on Firebase.

App.js



// ...

authHandler = (user) => {
  this.setState(
    { uid: user.uid },
    this.syncNotes
  )
}

// ...


Step 8: Signing out

Signing out when using Firebase for authentication is also simple - just call auth.signOut()! Once the promise returned by signOut has resolved, you can handle any additional cleanup. In Noteherder, we stop syncing with Firebase and set state.notes back to an empty object.

App.js



// ...

signOut = () => {
  auth
    .signOut()
    .then(
      () => {
        // stop syncing with Firebase
        base.removeBinding(this.ref)
        this.setState({ notes: {} })
      }
    )
}

// ...


Deployment: GitHub Pages

Deploying an app like Noteherder is fairly simple at this stage, as it runs entirely on the client side (the browser). create-react-app makes it even easier.

Note

create-react-app includes detailed instructions for deploying with GitHub Pages in the README.

Start by running the included build script.

user@localhost ~
 
yarn build     # build with yarn
npm run build  # build with npm

This builds the browser-ready version of our app in a build directory. (There are several aspects of our src directory that make it less than ideal for production use. For example, recall that our app is written using JSX, which browsers don’t understand.)

It also prints out these instructions:

user@localhost ~
 






The project was built assuming it is hosted at the server root.

To override this, specify the homepage in your package.json.

For example, add this to build it for GitHub Pages:



  "homepage" : "http://myname.github.io/myapp",

To host the app on GitHub Pages (learn more about GitHub Pages), add the “homepage” line to package.json, just like it says, substituting your GitHub user name and repository name. In my case:


  
  "name": "noteherder",
  "version": "0.1.0",
  "homepage": "http://xtbc17s2.github.io/noteherder",

  
  

Now run build again.

user@localhost ~
 
yarn build     # build with yarn
npm run build  # build with npm

This time, the output will include some more specific instructions.

user@localhost ~
 


















The build folder is ready to be deployed.

To publish it at http://xtbc17s2.github.io/noteherder, run:



  yarn add --dev gh-pages



Add the following script in your package.json.



    // ...

    "scripts": {

      // ...

      "predeploy": "npm run build",

      "deploy": "gh-pages -d build"

    }



Then run:



  yarn run deploy

Cool! Let’s add the gh-pages package.

user@localhost ~
 
yarn add --dev gh-pages          # with yarn, or...
npm install --save-dev gh-pages  # with npm

Now let’s add those two scripts to package.json.


  
  "scripts": {
    "start": "react-scripts start",
    "build": "react-scripts build",
    "test": "react-scripts test --env=jsdom",
    "eject": "react-scripts eject",
    "predeploy": "npm run build",
    "deploy": "gh-pages -d build"
  }

  
  

Now whenever you’re ready to deploy, you can just run yarn deploy!

user@localhost ~
 
yarn deploy     # deploy with yarn
npm run deploy  # deploy with npm

And your app will be available at the homepage listed in your package.json—in my case, http://xtbc17s2.github.io/noteherder.

Projects

Noteherder morning | afternoon

Homework

  • When you click on a note in the list, populate the form with the data from that note.
  • Make sure delete works once you do that.
  • Hint: Look up how componentWillReceiveProps works.
  • Run npm run deploy or yarn deploy to update the version on GitHub Pages.

Bonus Credit

  • Remove the “Save and new” button from NoteForm, and assign the same functionality to the “+” button in the sidebar.

Super Mega Bonus Credit

  • Add another authentication method to your app, such as Twitter, Facebook, Google, email/password, etc.
  • Remember, the Firebase documentation is your friend.