If you're using Cloud Firestore or Cloud Storage for Firebase, you're also using Security Rules. (If you're using the default rules instead of tailoring them to your app, this is where to start!) We're excited to announce that in the last few months we've released some substantial improvements to the tools for writing and debugging Rules, improvements to the Rules language itself, and increases to the size limits for Rules!. These are a few of the great new features. Check out the Security Rules Release Notes for a comprehensive list of everything we've released.
We've released several improvements to make the rules language more expressive and succinct. One particularly verbose pattern was comparing the new values of a document to existing values. The new Set type available in Rules is purpose-built for these comparisons, and also has methods for functionality you'd expect for a Set, like getting the intersection, union, or difference between Sets. For example:
Set type
Allow a user to create a document if the document has required and optional fields, but not others:
allow create: if (request.resource.data.keys().toSet() .hasOnly(["required","and","optional","keys"])
Sets come with == and in operators and hasAll, hasAny, hasOnly, difference, intersection, union, and size methods.
==
in
hasAll
hasAny
hasOnly
difference
intersection
union
size
Sets are most useful in conjunction with the Map class, and because the request and resource objects are both structured as maps, you're probably already familiar with it. Map recently got a few new methods, diff and get, that will hopefully open the door to more concise rules for everyone. Here's how they work:
Map
request
resource
diff
get
Map.diff() is called on one map, and takes the second map as an argument: map1.diff(map2). It returns a MapDiff object, and all of the MapDiff methods, like addedKeys, changedKeys, or affectedKeys return a Set object.
map1.diff(map2)
addedKeys
changedKeys
affectedKeys
Set
Map.diff() can solve some verbose patterns like checking which fields changed before and after a request. For example, this rule allows an update if the "maxLevel" field was the only field changed:
allow update: if request.resource.data.diff(resource.data).changedKeys().hasOnly(["maxLevel"]);
In the next example, posts have a field indicating the user role required to modify the post. We'll use Map.get() to get the "roleToEdit" field. If the document doesn't have the field, it will default to the "admin" role. Then we'll compare that to the role that's on the user's custom claims:
"roleToEdit"
"admin"
allow update, delete: if resource.data.get("roleToEdit", "admin") == request.auth.token.role;
Keep in mind that because Sets are not ordered but Lists are. You can convert a List to a Set, but you can't convert a Set to a List.
Local variables have been one of the most requested features in Rules, and they're now available within functions. You can declare a variable using the keyword let, and you can have up to 10 local variables per function.
let
Say you're commonly checking that a user meets the same three conditions before granting access: that they're an owner of the product or an admin user, that they successfully answered a challenge question, and that they meet the karma threshold.
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { match /products/{product} { allow read: if true; allow write: if (exists(/databases/$(database)/documents/admins/$(request.auth.uid)) || exists(/databases/$(database)/documents/product/owner/$(request.auth.uid))) && get(/databases/$(database)/documents/users/$(request.auth.uid)) .data.passChallenge == true && get(/databases/$(database)/documents/users/$(request.auth.uid)) .data.karma > 5; } match /categories/{category} { allow read: if true; allow write: if (exists(/databases/$(database)/documents/admins/$(request.auth.uid)) || exists(/databases/$(database)/documents/product/owner/$(request.auth.uid))) && get(/databases/$(database)/documents/users/$(request.auth.uid)) .data.passChallenge == true && get(/databases/$(database)/documents/users/$(request.auth.uid)) .data.karma > 5; } match /brands/{brand} { allow read, write: if (exists(/databases/$(database)/documents/admins/$(request.auth.uid)) || exists(/databases/$(database)/documents/product/owner/$(request.auth.uid))) && get(/databases/$(database)/documents/users/$(request.auth.uid)) .data.passChallenge == true && get(/databases/$(database)/documents/users/$(request.auth.uid)) .data.karma > 5; } } }
Those conditions, along with the paths I'm using for lookups can all now become variables in a function, which creates more readable rules:
rules_version = '2'; service cloud.firestore { match /databases/{database}/documents { function privilegedAccess(uid, product) { let adminDatabasePath = /databases/$(database)/documents/admins/$(uid); let userDatabasePath = /databases/$(database)/documents/users/$(uid); let ownerDatabasePath = /databases/$(database)/documents/$(product)/owner/$(uid); let isOwnerOrAdmin = exists(adminDatabasePath) || exists(ownerDatabasePath); let meetsChallenge = get(userDatabasePath).data.get("passChallenge", false) == true; let meetsKarmaThreshold = get(userDatabasePath).data.get("karma", 1) > 5; return isOwnerOrAdmin && meetsChallenge && meetsKarmaThreshold; } match /products/{product} { allow read: if true; allow write: if privilegedAccess(); } match /categories/{category} { allow read: if true; allow write: if privilegedAccess(); } match /brands/{brand} { allow read, write: if privilegedAccess(); } } }
You can see at a glance that the same conditions grant access to write to documents in the three different collections.
The updated version also uses map.get() to fetch the karma and passChallenge fields from the user data, which helps keep the new function concise. In this example, if there is no karma field for a user, then the get returns false. Keep in mind that Map.get() fetches a specific field, and is separate from the DocumentReference.get() that fetches a document.
map.get()
karma
passChallenge
Map.get()
DocumentReference.get()
This is the first time we've introduced an if/else control flow, and we hope it will make rules smoother and more powerful.
if/else
Here's an example of using a ternary operator to specify complex conditions for a write. A user can update a document in two cases: first, if they're an admin user, they need to either set the field overrideReason or approvedBy. Second, if they're not an admin user, then the update must include all the required fields:
overrideReason
approvedBy
allow update: if isAdminUser(request.auth.uid) ? request.resource.data.keys().toSet().hasAny(["overrideReason", "approvedBy"]) : request.resource.data.keys().toSet().hasAll(["all", "the", "required", "fields"])
It was possible to express this before the ternary, but this is a much more concise expression.
And finally, here's a feature for those of you with longer rules. Until now, rules files had to be smaller than 64 KB. (To be more specific, the compiled AST of the rules file had to be smaller than 64 KB, and you wouldn't know you were within the limit until you tried to deploy the rules.) This limit was holding some developers back, and once you reached the limit, you had to start making tradeoffs in your rules. We definitely wanted to fix this.
Since this is one of the limits that helps rules return a decision in nanoseconds, we wanted to find a way to increase the limit without sacrificing performance. We optimized how we compile and store the Rules file, and we were able to quadruple the limit to 256 KB!
The limits on rules are in place to keep rules fast enough to return a decision in nanoseconds, but we work hard to keep them workable. Let us know if you start to outgrow any of them
All of these features are informed by the feedback we hear from you about what's great, what's hard, and what's confusing about Firestore Security Rules, so keep letting us know what you think!
Over the last decade, mobile gaming has evolved from stacking colored blocks on a phone to encompass multidimensional and multiplayer experiences that engulf people in imaginative, high-fidelity worlds. As games have gotten more engaging, they’ve also become more complex to develop and maintain. Here at Firebase, we’re committed to providing game developers with the tools and resources you need to build, release and operate successful games that delight players.
We participated in the Google for Games Developer Summit in March, launched Unity and C++ SDKs for Cloud Firestore into open alpha, shipped improved workflows for Unity developers, debuted some new video tutorials, and wrote some new blogs too - so you can easily add Firebase to your game and get the most out of our platform.
We have more exciting updates to come, driven by your feedback and stories. Recently, we spoke to Gameloft to understand how they’ve been using Firebase.
Gameloft is one of the world’s largest gaming studios, employing 4,600 people across 19 global offices. Their vast portfolio consists of over 190 critically-acclaimed, award-winning games that are played by 80 million people each month. Gameloft develops original franchises, like Dungeon Hunter, and also partners with major entertainment studios like Disney.
With such a broad portfolio and audience, having the ability to monitor the stability of their games in a holistic way, and the agility to respond to issues quickly, is key to their success. However, they were lacking proper information about crashes, having a hard time reproducing crashes, and unforeseen technical issues were taking too long to track and fix, upsetting both their team and their players.
Gameloft realized they needed a robust crash reporting tool that could help them improve game quality in a more streamlined and efficient way, so they turned to Firebase Crashlytics.
Crashlytics aggregates crashes by root cause, highlights the impact on players, and provides contextual information on each issue, which helps Gameloft prioritize the crashes to fix first and speeds up their troubleshooting process. By using Crashlytics, Gameloft achieved a 10% reduction in users experiencing crashes overnight for one of their games (Overdrive City). According to Gameloft, this also led to a bump in the game’s Play Store rating and increased session duration by 16%! Find out what Gameloft did to drive these results with Crashlytics by reading the full case study. And learn more about how Firebase can help you build and grow your game, and see what other game studios are using Firebase.
With our announcement of the Firebase Crashlytics SDK Beta in February, we provided a path to help you remove all Fabric dependencies in your code, and complete your migration to Firebase. Since then, tens of thousands of apps have adopted the Beta SDK, and several hundreds of millions of end-users are using apps that include the SDK. Thank you to the adopters of our Beta SDKs; with your help, we have been able to polish and debug our SDK so we could ship it in its best possible launch state.
We are excited to announce that the Firebase Crashlytics SDK is now publicly available! Whether your app was migrated from Fabric or created in Firebase, we encourage you to upgrade to the official version of the Firebase Crashlytics SDK.
Now that the Firebase Crashlytics SDK is publicly available, we are deprecating the legacy Fabric SDK. The Fabric SDK will continue reporting your app's crashes until November 15, 2020. On this date, the Fabric SDK, and beta versions of the Firebase Crashlytics SDK, will no longer send crashes to your Firebase dashboard. To continue getting crash reports in the Firebase console, make sure your app has the following versions of the Firebase Crashlytics SDK, or newer: 17.0.0+ for Android, 4.0.0+ for iOS, and 6.15.0+ for Unity. Developers new to Crashlytics should begin with our getting started docs.
As always, we’d love to hear your feedback! Let us know what you think through our official support page!
Happy coding!
Update: To show respect for current events, we are postponing the launch of Firebase Live to June 23rd. Please take care of yourselves and your families. We look forward to connecting, sharing and learning together very soon. Stay tuned for updates.
Here at Firebase we are extremely proud of our avid developer community. We love interacting with so many of you at different events throughout the year. Our event plans have changed this year, but one thing that hasn’t changed is our commitment to you and all of the great stuff you build. So we’re trying something new to virtually bring everyone together, and are excited to introduce Firebase Live!
Firebase Live is a new 5-part weekly web series for developers consisting of talks, tips, and technical tutorials to help you increase productivity and collaboration among your team. Every Tuesday at 9:30am PST from June 23 through July 21, we’ll host a new video highlighting best practices and product updates. Everyone can watch the new video together and chat with members of the Firebase team and other developers along the way. After each video, Firebase experts will answer all your burning questions live. The topics include:
We will also be launching a new demo and codelabs with walkthrough videos so you can learn at your own pace and build your skills.
Excited about this weekly web series? Check out our event page and follow us on Twitter to join the conversation.
We may not be able to meet in person right now, but we’re still here to help you build amazing things.
Today we are extremely excited to introduce the Emulator UI to the Firebase community. The Emulator UI is a local web app that allows you to manage local emulators that make up the Firebase Local Emulator Suite.
Last year we released the Emulator Suite to simplify Firebase debugging and development. Until now, the Emulator Suite has only been exposed through the Firebase CLI. Today, the Emulator UI makes it even easier to develop locally with Firebase.
The Emulator UI aims to give you the freedom to develop on Firebase without concerns about billing or breaking a shared environment.
Need to create a function that triggers on the creation of a Firestore document? You can write that code, run it locally, and then create a document in the Emulator UI and see the trigger fire. What's better is that you can even see the logs of the local function to help with any debugging or even just to ensure a successful invocation. The goal is to provide you the safety of developing and testing your code without interfering with shared environments, waiting on deploys, or incurring billing costs.
The Emulator Suite doesn't just help with developer productivity, it also aims to help with team productivity as well. Having a local first environment also allows you to onboard new developers to your team without them needing to manage a cloud-connected Firebase project. Just run a few CLI commands and the Emulator Suite is up and running with local instances of Firebase services.
One of the biggest goals of the Emulator UI is to provide you with a local first workflow. Firebase has always been an online connected service. Most development required managing multiple projects, usually one for production and several others for prototyping, staging and development. The Emulator UI allows you to shift away from needing the online connected project and lets you start your development workflow with local instances.
The Emulator UI is not an exact copy of the online Firebase console. It's tailored for the functionality you need while developing. Here's just a small list of capabilities of the Emulator UI:
...and there's still more to come! We have plenty of features to simplify development planned for the future.
The emulators were designed to run locally. This means it has a looser set of restrictions on managing your data. We want to provide you the freedom to quickly delete and restore data without having to click through a million prompts. But we also don't want you to confuse a local environment with a production one. We took steps to clarify that you are operating in a local environment.
The Emulator UI introduces a new color into the Firebase palette. You'll notice as you navigate around, purple is the new primary color for all buttons, hover and selected states, and other primary actions in UI components. Even the favicon is purple! The online Firebase console uses a blue as the main active state color. With just a quick glance you'll be able to see the difference.
We also moved the navbar to the top for quick navigation. The Overview page displays the local ports of each instance as a reminder as well. We also provide a link to get your production/managed Firebase project, to help establish you are in a local environment versus a production one.
pssst… If you spend enough time in the Emulator UI, you also might find some new features in the Realtime Database data viewer.
Make sure you update the Firebase CLI to version 8.4.0 and run the following command:
firebase init
Select the products you need to initialize. Which means if this is a first time setup, you'll need to set up each Firebase product you need, such as Cloud Functions, Firestore and Realtime Database rules, as well as Hosting sites. Once everything you need is initialized, run the following command:
firebase emulators:start
This will start the Emulator UI on localhost:4000 by default. Open your browser to that URL and you'll see the Emulator UI up and running.
localhost:4000
We are so excited to see the community use the Emulator UI. Our hope is that it boosts your productivity and happiness when working with Firebase. But this is just the first step. We want to know what you are looking for in the Emulator Suite. Send us a tweet! We'd love to hear from you.