← Back to index

User Authentication - Firebase

Created: 2015-08-28 13:45  |  Source: https://www.firebase.com/docs/web/guide/user-auth.html

User Authentication

Firebase makes authentication easy. It can integrate with your existing login server, or authenticate users with only client-side code. It has built-in functionality for email & password auth and third-party providers such as Facebook, Twitter, GitHub, and Google.

Overview

Most applications need to know the identity of a user. Knowing a user's identity allows an app to provide a customized experience and grant them permissions to access their data. The process of proving a user's identity is called authentication. Firebase provides a full set of authentication options out-of-the-box.

When a user authenticates to a Firebase app, three things happen:

  • Information about the user is returned in callbacks on the client device. This allows you to customize your app's user experience for that specific user.
  • The user information returned contains a uid (a unique ID), which is guaranteed to be distinct across all providers, and to never change for a specific authenticated user.
  • The value of the auth variable in your app's Security and Firebase Rules becomes defined. This variable is null for unauthenticated users, but for authenticated users it is an object containing the user's unique (auth.uid) and potentially other data about the user. This allows you to securely control data access on a per-user basis.

Once a user authenticates to your app, Firebase manages their session, ensuring that the user is remembered across browser or application restarts.

Firebase apps have built-in support for logging in with email & password, social login providers such as Facebook, Google, Twitter, and GitHub, and single-session anonymous login. Apps that use Firebase's built-in auth services can handle user login entirely with client-side code, saving you time and the headache of operating your own backend.

If you have existing server-side authentication or single sign-on, you can also easily integrate it with Firebase. Download one of our helper libraries to generate authentication tokens on your own servers.

Simple Login Deprecated

Login is now a core feature of Firebase clients. Simple Login has been deprecated and documentation for this client is now available on Github.

Setup an Authentication Provider

Here is a list of the providers that Firebase applications support. Select the one that fits your needs, and follow the provider-specific steps to set it up.

Platform Description
Custom Generate your own login tokens. Use this to integrate with existing authentication systems. You can also use this to authenticate server-side workers.
Email & Password Let Firebase manage passwords for you. Register and authenticate users by email & password.
Anonymous Build user-centric functionality without requiring users to share their personal information. Anonymous authentication generates a unique identifier for each user that lasts as long as their session.
Facebook Authenticate users with Facebook by writing only client-side code.
Twitter Authenticate users with Twitter by writing only client-side code.
GitHub Authenticate users with GitHub by writing only client-side code.
Google Authenticate users with Google by writing only client-side code.

Configuring Login

For security reasons, if you're using a web-based OAuth flow (Facebook, Twitter, Github, or Google), only domains that you whitelist are allowed to initiate authentication for your app. This does not apply to Email & Password, Anonymous, or Custom authentication methods. All Firebase applications have localhost and 127.0.0.1 enabled by default for local development and testing. They also include https://<YOUR-FIREBASE-APP>.firebaseapp.com as an authorized origin in case you want to utilize Firebase Hosting. Add more authorized origins to enable authentication from domains where you host your app.

  1. Navigate to the App Dashboard for your application.
  2. Open the Login & Auth tab.
  3. Add all of the domains that host your app to the Authorized Domains field.

Enabling Providers

Next, you need to enable the provider for your Firebase app.

  1. Go to the Account Dashboard.
  2. Select your Firebase app.
  3. Choose the Login & Auth tab.
  4. Find the Authentication Providers section.
  5. Select a tab for a provider and enable authentication.
  6. If you're using a social login provider, like Facebook, copy your client ID and secret into the form.

The Firebase app is all set up for your provider. It's time to code.

Monitoring User Authentication State

Use the onAuth() method to listen for changes in user authentication state.

  1. // Create a callback which logs the current auth state
  2. function authDataCallbackauthData
  3. authData
  4. console"User " authData" is logged in with " authDataprovider
  5. console"User is logged out"
  6. // Register the callback to be fired every time auth state changes
  7. Firebase"https://<YOUR-FIREBASE-APP>.firebaseio.com"
  8. onAuthauthDataCallback

To stop listening for changes, you can use offAuth().

  1. offAuthauthDataCallback

Additionally, you can use the getAuth() method to synchronously check authentication state.

  1. Firebase"https://<YOUR-FIREBASE-APP>.firebaseio.com"
  2. authData getAuth
  3. authData
  4. console"User " authData" is logged in with " authDataprovider
  5. console"User is logged out"

Persisting User Auth State

When a user authenticates, the default session length is 24 hours from initial authentication. This means that the user's authentication state will automatically be persisted between page loads. You can configure the session length by navigating to the Login & Auth section of your Firebase App Dashboard and configuring the Session Length dropdown on the top right. Every auth provider has an optional remember parameter.

If remember is set to default, the session will last for the time you've configured in your App Dashboard. You can set remember to sessionOnly to limit persistence to the lifetime of the current window. The session length will be the same across all authentication providers you're using. If you're using an onAuth() callback, it will be triggered when a user reloads a page as long as their session has not expired.

Logging Users In

The code to authenticate a user varies by provider and transport method, but they all have similar signatures and accept a callback function. Use it to handle errors and process the results of a successful login.

  1. // Create a callback to handle the result of the authentication
  2. function authHandlererror authData
  3. error
  4. console"Login Failed!" error
  5. console"Authenticated successfully with payload:" authData
  6. // Authenticate users with a custom authentication token
  7. authWithCustomToken"<token>" authHandler
  8. // Alternatively, authenticate users anonymously
  9. authAnonymouslyauthHandler
  10. // Or with an email/password combination
  11. authWithPassword
  12. email 'bobtony@firebase.com'
  13. password 'correcthorsebatterystaple'
  14. authHandler
  15. // Or via popular OAuth providers ("facebook", "github", "google", or "twitter")
  16. authWithOAuthPopup"<provider>" authHandler
  17. authWithOAuthRedirect"<provider>" authHandler

Tokens issued to the authenticated users are valid for 24 hours by default. You can change this from the Login & Auth tab on the App Dashboard.

Note: Looking to authenticate connections in Node.js? Refer to the documentation for Firebase.authWithCustomToken().

Logging Users Out

Calling unauth() invalidates the user's token and logs them out of your application:

  1. unauth

If you had previously used the onAuth() method to listen for authentication state, your callback would now be invoked with null for authData.

Storing User Data

Internally, Firebase apps generate JSON Web Tokens (JWTs) and create authenticated sessions by calling Firebase.loginWithCustomToken() with those tokens. Each user is assigned a uid (unique ID), a String which is guaranteed to be distinct across all providers, and to never change for a specific authenticated user.

Applications do not automatically store profile or user state. To persist user data you must save it to your database. The callback function will return an object containing the data for the authenticated user, which you can then write to your database.

In the code sample below, we authenticate the user, and then store user data in the path https://<YOUR-FIREBASE-APP>.firebaseio.com/users/<uid>, where users/ is any arbitrary path to store user data, and <uid> represents the unique id obtained from the authentication data.

This code saves a user's information after they authenticate, using the uid as the key for the user's profile.

  1. // we would probably save a profile when we register new users on our site
  2. // we could also read the profile to see if it's null
  3. // here we will just simulate this with an isNewUser boolean
  4. isNewUser
  5. Firebase"https://<YOUR-FIREBASE-APP>.firebaseio.com"
  6. onAuthfunctionauthData
  7. authData isNewUser
  8. // save the user's profile into the database so we can list users,
  9. // use them in Security and Firebase Rules, and show profiles
  10. child"users"childauthData
  11. provider authDataprovider
  12. getNameauthData
  13. // find a suitable name based on the meta info given by each provider
  14. function getNameauthData
  15. switchauthDataprovider
  16. 'password'
  17. return authDatapasswordemailreplace/@.*/
  18. 'twitter'
  19. return authDatatwitterdisplayName
  20. 'facebook'
  21. return authDatafacebookdisplayName

When a user is saved using the above code, the /users/ node looks something like this:

  1. "users"
  2. "c20f3bea-906f-4cd6-b38a-175bb4506287"
  3. "provider""password"
  4. "name""bobtony"
  5. "1376ecee-fc04-4809-9de9-ff41747e12a7"
  6. "provider""twitter"
  7. "name""Andrew Lee"
  8. "9550be91-ab5d-41c1-8cd0-24c883586071"
  9. "provider""facebook"
  10. "name""James Tamplin"

Since the user's uid property is unique, it is the recommended key for storing users.

Dealing with Popups and Redirects

Firefeed
For a robust example of authenticating users with Firebase in a production environment, showcasing both third-party authentication as well as session persistence, see the Firefeed repository on Github.

Firebase apps support three different ways to authenticate with OAuth providers - via pop-up, browser redirect, or credential login.

Third-party authentication methods use a browser pop-up window, or browser redirect, to prompt the user to sign-in, approve the application, and return the user's data to the requesting application.

Most modern browsers block pop-up windows unless they are invoked by direct user action. Therefore, we should only invoke the authWithOAuthPopup() method for third-party authentication upon the user's click.

Note: Browser popups and redirects are not available on all platforms or browser environments. Popups are not available in Chrome for iOS, iOS Preview Panes, or local, file:// URLs. Redirects are not available in PhoneGap / Cordova, or local, file:// URLs. Therefore, it is recommended that you use a combination of both authentication methods to cover all environments:

  1. Firebase"https://<YOUR-FIREBASE-APP>.firebaseio.com"
  2. // prefer pop-ups, so we don't navigate away from the page
  3. authWithOAuthPopup"google"functionerror authData
  4. error
  5. error"TRANSPORT_UNAVAILABLE"
  6. // fall-back to browser redirects, and pick up the session
  7. // automatically when we come back to the origin page
  8. authWithOAuthRedirect"google"functionerror/* ... */
  9. authData
  10. // user authenticated with Firebase

Handling Errors

When your app calls an authentication method, you pass it a callback. This function is invoked with the result of the authentication attempt, either an error or authData object.

All errors are Error objects containing at least code and message attributes. In some cases, additional information will be provided via the details attribute. For example:

  1. "TRANSPORT_UNAVAILABLE"
  2. message"There are no login transports available for the requested method."
  3. details"More details about the specific error here."

In some cases you may want to explicitly check for specific errors to notify your user, or prompt them to login again. For example, if you're using email & password authentication, you would want to check for an invalid email or password:

  1. Firebase"https://<YOUR-FIREBASE-APP>.firebaseio.com"
  2. authWithPassword
  3. email "bobtony@firebase.com"
  4. password "invalid-password"
  5. functionerror authData
  6. error
  7. switcherror
  8. "INVALID_EMAIL"
  9. console"The specified user account email is invalid."
  10. break
  11. "INVALID_PASSWORD"
  12. console"The specified user account password is incorrect."
  13. break
  14. "INVALID_USER"
  15. console"The specified user account does not exist."
  16. break
  17. default
  18. console"Error logging user in:" error
  19. console"Authenticated successfully with payload:" authData

Authentication Sample App

Try it on JSFiddle

This interactive demo demonstrates registration and login. Once the user is logged in, we save that user information to a users location in our database and take them to a screen showing all the registered users in the system.

Full Error Listing

Error Code Description
AUTHENTICATION_DISABLED The requested authentication provider is disabled for this Firebase application.
EMAIL_TAKEN The new user account cannot be created because the specified email address is already in use.
INVALID_ARGUMENTS The specified credentials are malformed or incomplete. Please refer to the error message, error details, and Firebase documentation for the required arguments for authenticating with this provider.
INVALID_CONFIGURATION The requested authentication provider is misconfigured, and the request cannot complete. Please confirm that the provider's client ID and secret are correct in your App Dashboard and the app is properly set up on the provider's website.
INVALID_CREDENTIALS The specified authentication credentials are invalid. This may occur when credentials are malformed or expired.
INVALID_EMAIL The specified email is not a valid email.
INVALID_ORIGIN A security error occurred while processing the authentication request. The web origin for the request is not in your list of approved request origins. To approve this origin, visit the Login & Auth tab in your App Dashboard.
INVALID_PASSWORD The specified user account password is incorrect.
INVALID_PROVIDER The requested authentication provider does not exist. Please consult the Firebase Authentication documentation for a list of supported providers.
INVALID_TOKEN The specified authentication token is invalid. This can occur when the token is malformed, expired, or the Firebase app secret that was used to generate it has been revoked.
INVALID_USER The specified user account does not exist.
NETWORK_ERROR An error occurred while attempting to contact the authentication server.
PROVIDER_ERROR A third-party provider error occurred. Please refer to the error message and error details for more information.
TRANSPORT_UNAVAILABLE The requested login method is not available in the user's browser environment. Popups are not available in Chrome for iOS, iOS Preview Panes, or local, file:// URLs. Redirects are not available in PhoneGap / Cordova, or local, file:// URLs.
UNKNOWN_ERROR An unknown error occurred. Please refer to the error message and error details for more information.
USER_CANCELLED The current authentication request was cancelled by the user.
USER_DENIED The user did not authorize the application. This error can occur when the user has cancelled an OAuth authentication request.
Page 2

It's a JSON Tree

All Firebase database data is stored as JSON objects. There are no tables or records. When we add data to the JSON tree, it becomes a key in the existing JSON structure. For example, if we added a child named widgets under users/mchen/, our data looks as follows:

{
  "users": {
    "mchen": {
      "friends": { "brinchen": true },
      "name": "Mary Chen",
      // our child node appears in the existing JSON tree
      "widgets": { "one": true, "three": true }
    },
    "brinchen": { ... },
    "hmadi": { ... }
  }
}

Creating a Firebase database Reference

To read and write database data, we first create a reference to the Firebase database. This data to be loaded is specified with a URL.

new Firebase('https://docs-examples.firebaseio.com/web/data');
Data Limits
A child node's key cannot be longer than 768 bytes, nor deeper than 32 levels. It can include any unicode characters except for . $ # [ ] / and ASCII control characters 0-31 and 127.

Creating a reference does not create a connection to the server or begin downloading data. Data is not fetched until a read or write operation is invoked. Once it is retrieved, it stays cached locally until the last event listener is removed.

Firebase provides an application which displays a visual representation of the database data and provides tools for simple administrative tasks. We refer to this as the app dashboard. All the data in this guide is stored in the docs-examples Firebase database; a read-only version of the app dashboard can be viewed by going to the Firebase URL in a browser.

It's possible to directly access child nodes in the data as well. For example, to point to Mary Chen's name, simply append users/mchen/name to the URL:

new Firebase("https://docs-examples.firebaseio.com/web/data/users/mchen/name");

We can achieve the same result from an existing parent reference by using the child() API call:

var rootRef = new Firebase('https://docs-examples.firebaseio.com/web/data');
rootRef.child('users/mchen/name');

In a similar fashion, it's possible to drill down directly to the database data in the application Dashboard by simply adding the child path to the URL.

Data can also be visualized directly in Chrome's DevTools by using Vulcan, an extension built by the Firebase team. Vulcan allows you to create, read, update and delete data for a specific Firebase database, as well as modify the structure of your Firebase database by adding a child, a branch, or arbitrary JSON to any node. Visit the Chrome App Store to download Vulcan and view the open source repository on GitHub.

Arrays in a Firebase database

Firebase databases have no native support for arrays. If we try to store an array, it really gets stored as an "object" with integers as the key names.

// we send this
['hello', 'world']
// Firebase databases store this
{0: 'hello', 1: 'world'}

However, to help developers that are storing arrays in a Firebase database, when data is read using val() or via the REST api, if the data looks like an array, Firebase clients will render it as an array. In particular, if all of the keys are integers, and more than half of the keys between 0 and the maximum key in the object have non-empty values, then Firebase clients will render it as an array. This latter part is important to keep in mind.

// we send this
['a', 'b', 'c', 'd', 'e']
// Firebase databases store this
{0: 'a', 1: 'b', 2: 'c', 3: 'd', 4: 'e'}

// since the keys are numeric and sequential,
// if we query the data, we get this
['a', 'b', 'c', 'd', 'e']

// however, if we then delete a, b, and d,
// they are no longer mostly sequential, so
// we do not get back an array
{2: 'c', 4: 'e'}

It's not currently possible to change or prevent this behavior. Hopefully understanding it will make it easier to see what one can and can't do when storing array-like data.

Why not just provide full array support? Since array indices are not permanent or unique, concurrent real-time editing will always be problematic.

Consider, for example, if three users simultaneously updated an array on a remote service. If user A attempts to change the value at key 2, user B attempts to move it, and user C attempts to change it, the results could be disastrous. For example, among many other ways this could fail, here's one:

// starting data
['a', 'b', 'c', 'd', 'e']

// record at key 2 moved to position 5 by user A
// record at key 2 is removed by user B
// record at key 2 is updated by user C to foo

// what ideally should have happened
['a', 'b', 'd', 'e']

// what actually happened
['a', 'c', 'foo', 'b']

So when is it okay to use an array? If all of the following are true, it's okay to store the array in your Firebase database:

  • Only one client is capable of writing to the data at a time
  • To remove keys, we save the entire array instead of using remove()
  • We take extra care when referring to anything by array index (a mutable key)
Read more about arrays in our blog post: Best Practices: Arrays in Firebase.

Limitations and Restrictions

A quick reference to limitations in data storage and read ops in a Firebase database.

Description Limit Notes
Depth of child nodes 32
Length of a key 768 bytes UTF-8 encoded, cannot contain . $ # [ ] / or ASCII control characters 0-31 or 127
Size of one child value 10mb UTF-8 encoded
Write from SDK 16mb UTF-8 encoded
Write from REST 256mb
Nodes in a read operation 100 million

Backups and Restores

Firebase performs automated backups of all Firebase databases daily. The backups are stored for 60 days at an off-site facility. Since these backups are done at the hardware level, they do not affect your bandwidth usage or performance. These backups are primarily for disaster recovery, but can be made available to developers on a case-by-case basis for purposes of emergency restores.

Firebase also offers optional, private backups to a Google Cloud Storage (GCS) bucket or an Amazon Simple Storage Solution (S3) bucket, for databases which have upgraded to the Bonfire, Blaze, or Inferno plan. Since these backups are done at the hardware level, they do not count against your bandwidth usage and do not affect performance of the database. Email support@firebase.com to enable this feature for your database.

It is also possible to create manual backups via the REST API. For databases with less than 200MB of data, this can be done by simply requesting the entire database using the root URL. For larger instances, you should break up your data by path or by key and retrieve it in smaller chunks.

Keep in mind that backing up data via the REST API does count against your bandwidth usage, and it can affect performance. Backups of large data (gigabytes) should be spread over a large time frame to reduce the impact on clients connected to the database.

For more information about chunking data and creating backups of big data, see the REST API's query parameters. Used together, startAt, limitToFirst, and shallow=true arguments can be used to index keys for any amount of data, and retrieve it in manageable segments.