DEV Community

Cover image for Dart-Powered Firebase Security: Exploring Flood's Drop and Automate Modules
Jake Boychenko
Jake Boychenko

Posted on

Dart-Powered Firebase Security: Exploring Flood's Drop and Automate Modules

As Flutter developers, we're always on the lookout for tools that can simplify our workflow and enhance our productivity. When it comes to integrating Firebase, one of the most tedious and error-prone tasks is managing Firebase security rules. Enter Flood, an open-source Flutter toolkit that's revolutionizing how we handle Firebase Security Rules. In this article, we'll explore how Flood's Drop and Automate modules work together to generate and deploy Firebase Security Rules automatically, saving you time and reducing potential security vulnerabilities.


The Firebase Security Rules Challenge

Before we dive into Flood, let's remind ourselves why Firebase Security Rules can be challenging:

  1. They use a separate syntax from your app's code.
  2. It's easy to introduce inconsistencies between your app logic and security rules.
  3. Testing and debugging can be cumbersome.
  4. Deploying rules manually is tedious.

Now, let's see how Flood addresses these issues.

Introducing Flood

Flood is a comprehensive Flutter toolkit designed to accelerate app development. It offers modules for styling, navigation, form handling, and much more. Today, we're focusing on two key modules: Drop and Automate.

Defining Security Rules with Drop

Drop is Flood's data modeling module. It allows you to define your data structures and security rules directly in Dart. Let's start by defining two ValueObjects: Todo and User.

class Todo extends ValueObject {
  static const nameField = 'name';
  late final nameProperty = field<String>(name: nameField).withDisplayName('Name').isNotBlank();

  static const descriptionField = 'description';
  late final descriptionProperty = field<String>(name: descriptionField).withDisplayName('Description').multiline();

  static const completedField = 'completed';
  late final completedProperty = field<bool>(name: completedField).withFallback(() => false);

  static const ownerField = 'owner';
  late final ownerProperty = reference<UserEntity>(name: ownerField).required();

  @override
  late final List<ValueObjectBehavior> behaviors = [
    nameProperty,
    descriptionProperty,
    completedProperty,
    ownerProperty,
    creationTime(),
  ];
}

class User extends ValueObject {
  static const nameField = 'name';
  late final nameProperty = field<String>(name: nameField).withDisplayName('Name').isNotBlank();

  static const emailField = 'email';
  late final emailProperty = field<String>(name: emailField).withDisplayName('Email').isNotBlank().isEmail();

  @override
  late final List<ValueObjectBehavior> behaviors = [
    nameProperty,
    emailProperty,
    creationTime(),
  ];
}

Enter fullscreen mode Exit fullscreen mode

In Flood, ValueObjects are the building blocks of your data model. They define the structure and validation rules for your data:

  • Each property (like nameProperty or emailProperty) represents a field in your data model.
  • Modifiers like withDisplayName(), isNotBlank(), and isEmail() add metadata and validation rules to the fields.
  • The behaviors list combines all properties and any additional behaviors (like creationTime()).

These ValueObjects provide a clear, type-safe way to define your data structure and ensure data integrity through built-in validation.

Now that we have our ValueObjects defined, let's create repositories for them with security rules:

class TodoRepository with IsRepositoryWrapper {
  @override
  late Repository repository = Repository.forType<TodoEntity, Todo>(...)
    .adapting('todo')
    .withSecurity(RepositorySecurity.all(
        Permission.admin |
        Permission.equals(PermissionField.propertyName(Todo.ownerField), PermissionField.loggedInUserId)
      ));
}

class UserRepository with IsRepositoryWrapper {
  @override
  late Repository repository = Repository.forType<UserEntity, User>(...)
    .adapting('user')
    .withSecurity(RepositorySecurity(
        read: Permission.authenticated,
        create: Permission.equals(PermissionField.loggedInUserId, PermissionField.entityId),
        update: Permission.equals(PermissionField.loggedInUserId, PermissionField.entityId),
        delete: Permission.none,
      ));
}

Enter fullscreen mode Exit fullscreen mode

In these examples:

  1. The TodoRepository allows access if the user is an admin OR if they are the owner of the todo item. This applies to all operations (read, create, update, delete).
  2. The UserRepository has more granular security:
    • Read access is granted to any authenticated user.
    • Create and update operations are only allowed if the logged-in user's ID matches the entity ID (i.e., users can only create/edit their own profiles).
    • Delete operations are not allowed for any user.

This Dart code is type-safe, easy to read, and directly integrated with your app's logic. It provides a clear and concise way to define security rules that are closely tied to your data models.

Local Security Enforcement

One of the powerful features of Flood's withSecurity modifier is that it doesn't just generate rules for Firebase – it also enforces these rules locally while you're developing and testing your app. This local enforcement is crucial for catching security issues early in the development process.

Flood uses the concept of environments to determine how to handle various aspects of your app, including security rules. The main environments are:

  • testing: Simulates all services in-memory.
  • device: Simulates services on the device's file system.
  • production: Uses online resources like Firebase.

When you're in the testing or device environment, Flood will enforce the security rules you've defined with withSecurity directly in your app. This means you can validate whether your security rules work as expected before you ever deploy them to Firebase.

Generating Firebase Rules with Automate

Now that we've defined and validated our security rules in Dart, how do we turn them into Firebase Security Rules? This is where the Automate module comes in. Automate can analyze your Drop repositories and generate the corresponding Firebase Security Rules.

To generate and deploy the rules, you simply run:

dart tool/automate.dart deploy production
Enter fullscreen mode Exit fullscreen mode

Behind the scenes, Automate will:

  1. Analyze all your Drop repositories
  2. Generate Firestore security rules
  3. Show you a diff of the changes
  4. Deploy the rules to your Firebase project (after your confirmation)

Benefits of Automated Rule Generation

  1. Consistency: Your security rules are always in sync with your app's data models and logic.
  2. Type Safety: Leverage Dart's type system to catch potential issues early.
  3. Easier Testing: Test your security rules alongside your app code.
  4. Version Control: Your security rules are now part of your codebase, making changes trackable.
  5. Time-saving: No more manual rule writing and deployment.

Conclusion

Automating Firebase Security Rule generation with Flood's Drop and Automate modules offers a powerful way to streamline your Flutter development process. By defining your security rules in Dart, you gain type safety, easier testing, and seamless integration with your app's logic. The automatic generation and deployment of these rules save time and reduce the risk of security inconsistencies.

If you're building Flutter apps with Firebase, I highly recommend giving Flood a try. It's not just about security rules – Flood offers a comprehensive suite of tools to accelerate your Flutter development across the board.

To learn more about implementing data security in your Flood projects, including advanced techniques and best practices, check out the Data Security guide in the Flood documentation. This guide will provide you with in-depth information on creating robust security rules, handling user permissions, and setting up admin users in Firebase.

Join the growing community of developers who've discovered the power of Flood for streamlined, secure Flutter development!

Top comments (0)