天天看點

firebase database: user based securityUser Based Security

User Based Security

This document revisits concepts from Securing Your Data, incorporating the predefined 

auth

 variable to create a complete solution for securing our data.

Integrating Authentication

Firebase Authentication integrates with the Firebase Realtime Database to allow you to control data access on a per-user basis.

Once a user authenticates, the 

auth

 variable in your Firebase Database Rules rules will be populated with the user's information. This information includes their unique identifier (

uid

) as well as linked account data, such as a Facebook id or an email address, and other info. If you implement a custom auth provider, you can add your own fields to your user's auth payload.

This guide explains how to combine the Firebase Realtime Database Rules language with authentication information about your users. By combining these two concepts, you can control access to data based on user identity.

The 

auth

 Variable

The predefined 

auth

 variable in the rules is null before authentication takes place. Once a user is authenticated with Firebase Authentication, it will contain the following attributes:

provider The authentication method used ("password", "anonymous", "facebook", "github", "google", or "twitter").
uid A unique user id, guaranteed to be unique across all providers.
token The contents of the Firebase Auth ID token. See the reference documentation for 

auth.token

 for more details.

Here is an example rule that uses the 

auth

 variable to ensure that each user can only write to a user-specific path:

{

  "rules" : {

    "users" : {

      "$user_id" : {

        // grants write access to the owner of this user account

        // whose uid must exactly match the key ($user_id)

        ".write" : "$user_id === auth.uid"

      }

    }

  }

}

Structuring Your Database

It is sometimes helpful to structure your database in a way that makes writing security rules easier. As an example, one common pattern for storing user data in the Realtime Database is to store all of your users in a single 

users

 node whose children are the 

uid

 values for every user. If you wanted to restrict access to this data such that only the logged-in user can see their own data, your rules would look something like this:

{

  "rules" : {

    "users" : {

      "$uid" : {

        ".read" : "auth != null && auth.uid == $uid"

      }

    }

  }

}

Working with Custom Authentication Tokens

For developers who want extra control, Firebase Authentication allows developers to create their own custom authentication tokens on their servers.

Developers creating their own custom authentication tokens can optionally add additional claims to these tokens. These additional claims will become present on the 

auth.token

 variable in your rules. Here is an example of rules that make use of the 

hasEmergencyTowel

 custom claim:

{

  "rules" : {

    "frood" : {

      // A towel is about the most massively useful thing an interstellar

      // hitchhiker can have

      ".read" : "auth.token.hasEmergencyTowel === true"

    }

  }

}

The Admin SDK also lets you set custom token claims to help define user roles for access control. See Control Access with Custom Claims and Security Rules.

Revisiting the Chat Example

Let's build on the chat example from Securing Your Data and add in some user authentication, pulling all of these concepts together into a working app:

{

  "rules" : {

    "room_names" : {

      // any logged in user can get a list of room names

      ".read" : "auth !== null" ,

      "$room_id" : {

        // this is just for documenting the structure of rooms, since

        // they are read-only and no write rule allows this to be set

        ".validate" : "newData.isString()"

      }

    },

    "members" : {

        // I can join or leave any room (otherwise it would be a boring demo)

        // I can have a different name in each room just for fun

        "$room_id" : {

          // any member can read the list of member names

          ".read" : "data.child(auth.uid).exists()" ,

          // room must already exist to add a member

          ".validate" : "root.child('room_names/'+$room_id).exists()" ,

          "$user_id" : {

              ".write" : "auth.uid === $user_id" ,

              ".validate" : "newData.isString() && newData.val().length > 0 && newData.val().length < 20"

          }

        }

    },

    "messages" : {

      "$room_id" : {

        // the list of messages for a room can be read by any member

        ".read" : "root.child('members/'+$room_id+'/'+auth.uid).exists()" ,

        // room we want to write a message to must be valid

        ".validate" : "root.child('room_names/'+$room_id).exists()" ,

        "$message_id" : {

          // a new message can be created if it does not exist, but it

          // cannot be modified or deleted

          // any member of a room can write a new message

          ".write" : "root.child('members/'+$room_id+'/'+auth.uid).exists() && !data.exists() && newData.exists()" ,

          // the room attribute must be a valid key in room_names/ (the room must exist)

          // the object to write must have a name, message, and timestamp

          ".validate" : "newData.hasChildren(['user', 'message', 'timestamp'])" ,

          // the message must be written by logged in user

          "user" : {

              ".validate" : "newData.val() === auth.uid"

          },

          // the message must be longer than 0 chars and less than 50

          "message" : { ".validate" : "newData.isString() && newData.val().length > 0 && newData.val().length < 50" },

          // messages cannot be added in the past or the future

          // clients should use firebase.database.ServerValue.TIMESTAMP

          // to ensure accurate timestamps

          "timestamp" : { ".validate" : "newData.val() <= now" },

          // no other fields can be included in a message

          "$other" : { ".validate" : false }

        }

      }

    }

  }

} Try it on JSFiddle: Feel free to test it out!  This interactive demo, written in JavaScript, authenticates to GitHub's OAuth API using Firebase Authentication, and enforces all of the Firebase Realtime Database Rules above in a functional chat demo.

Next Steps

  • Learn more about specifying indexes using rules.