Building Secure Flutter Apps with Firebase: Using Service Account Keys

Building Secure Flutter Apps with Firebase: Using Service Account Keys
Secure Flutter Apps with Firebase Service Account Keys

Building secure apps is crucial, especially when dealing with sensitive data or backend services. In a Flutter app, interacting with Firebase, whether to manage users or perform backend tasks requires an efficient and secure authentication method.

This is where Firebase Service Account Keys step in, they enable our app to communicate securely with Firebase without needing user credentials every time. This makes them perfect for backend processes, automated tasks, and more.

In this codelab, we will learn using Firebase Service Account Keys with Flutter. We’ll introduce a practical solution to simplify backend operations along the way.

N.B., if you are new to Flutter, please review the official documentation to learn about it.

Table of Contents

  • What Are Firebase Service Account Keys?
  • Why Use Service Account Keys with Flutter?
  • Initializing Firebase App with Service Account Keys
  • Common Issues and Solutions
  • Conclusion

What are Firebase Service Account Keys?

Firebase Service Account Keys are authentication credentials that provide secure, programmatic access to our Firebase console services. They allow our app to perform server-side operations without needing user credentials. Firebase generated these keys and served as “master keys” to authorize specific services to interact with Firebase’s backend.

The key is a JSON file containing private credentials that lets Firebase identify the app or service accessing its resources. They allow us to automate key functions such as sending notifications or updating user records without user involvement.

Why Use Service Account Keys with Flutter?

When building a Flutter app, we often need secure access to Firebase service to perform backend operations instead of requiring manual login for any backed operations such as managing user roles or sending notifications. Service Account Keys streamline these tasks.

Service Account Keys vs. Google Services JSON File

  • Google Services JSON File: This is used in client apps to access Firebase services with user authentication.
  • Service Account Key: This is used for server-side or machine-to-machine communication. It allows our app to perform backend tasks autonomously without user interaction.

Initializing Firebase App with Service Account Keys 

The Firebase Dart Admin Auth SDK plugin simplifies the process of working with Service Account Keys by offering a robust and flexible set of tools to perform authentication procedures within Dart or Flutter projects. 

This is a Dart implementation of Firebase admin authentication, and this SDK is designed to enable developers to interact with Firebase authentication services allowing them to perform authentication functions like custom token generation, and use management tasks from their applications.

Prerequisites

  • Ensure you have the Flutter and Dart SDK installed in your system.
  • The Dart SDK should be at the latest version (>3.2)

Set up the Flutter project

Download the starter project containing the prebuilt UI and minimal configuration from here.

N.B., for more details on how to set up your Flutter project read this. I use VSCode but you can choose between other editors.

Open it in your editor, then build and run the app:

The file structure of the starter project looks like this:

The next step is to set up the Service Account Key and later the Firebase Dart Admin Auth SDK in our project.

Setting Up Firebase Service Account Keys

Step 1:
Create or Select a Firebase Project: Head to the Firebase Console to create or select a project.

Step 2:
Generate a Service Account Key:

  • Go to Project Settings > Service Accounts:
  • Click Generate New Private Key and download the JSON file:

Step 3:
Store the Key Securely: Place the key file in our Flutter project directory, ensuring it’s not exposed in version control (e.g., .gitignore), and add it in the  pubspec flutter section:

flutter:
    uses-material-design: true
    assets:
        assets/

N.B., Take a note of your firebase project_id, we’ll be needing that next.

Installing the Auth SDK

Add the Firebase Dart Admin Auth SDK in the pubspec dependencies:

dependencies:
  firebase_dart_admin_auth_sdk: ^latest_version

Adding Project ID and API Key to the Auth SDK

Before initializing the SDK, we need to update the plugin with the project id and api key, so we have to use our Project ID from earlier and get Web API Key from Project Settings > General as below:

Next, we need to add these details into the plugin’s main.dart as below:

import 'package:firebase_dart_admin_auth_sdk/src/firebase_auth.dart';

void main() async {
  final auth =
      FirebaseAuth(apiKey: 'YOUR_API_KEY', projectId: 'YOUR_PROJECT_ID');

  try {
    // Sign up a new user
    final newUser = await auth.createUserWithEmailAndPassword(
        'newuser@example.com', 'password123');
    print('User created: ${newUser?.user.displayName}');
    print('User created: ${newUser?.user.email}');

    // Sign in with the new user
    final userCredential = await auth.signInWithEmailAndPassword(
        'newuser@example.com', 'password123');
    print('Signed in: ${userCredential?.user.email}');
  } catch (e) {
    print('Error: $e');
  }
}

Now we are ready to initialize the Firebase Dart Admin Auth SDK and ready to add these details into our main.dart.

Initialize Firebase with the Service Account Key

Use the following code to authenticate our app with Firebase using the downloaded key:

import 'package:flutter/material.dart';
import 'package:firebase_dart_admin_auth_sdk/firebase_dart_admin_auth_sdk.dart';
import 'package:flutter/services.dart';

Future<void> main() async {
  // 1
  WidgetsFlutterBinding.ensureInitialized();
  // 2
  String serviceAccountContent = await rootBundle.loadString(
      'assets/service_account.json');//Add your own JSON service account
  
  // 3
  await FirebaseApp.initializeAppWithServiceAccount(
    serviceAccountContent: serviceAccountContent,
    serviceAccountKeyFilePath: '',
  );

  // Look for N.B below
  FirebaseApp.instance.getAuth(); 

  runApp(const MyApp());
}

In the above code:

  1. Ensures that we have an instance of the WidgetsBinding, which is required to use platform channels to call the native code. You can read more here.
  2. Here we are retrieving a string from the service_account.json asset.
  3. Initializes Firebase with the service account content along with the web api key and project id from previous step.

N.B., Make sure FirebaseApp.instance.getAuth() is added, else it might result into null response later in the codelab.

Register New User

In a real-world scenario, imagine we’re building an admin dashboard for our app. We want to allow admins to manage users without needing their Firebase credentials. 

With the firebase_dart_admin_auth_sdk plugin, we can automate this process. Every time a new user is registered in our system, our app can automatically create a corresponding user in Firebase, and set roles, no manual intervention is needed.

Here’s an example:

…

// 1
FirebaseAuth? firebaseAuth;

…

@override
  void initState() {
    firebaseAuth = FirebaseApp.firebaseAuth;
    super.initState();
  }


…

Future<void> registerNewUser() async {
    try {
      // 2
      var userCredential = await firebaseAuth?.createUserWithEmailAndPassword(
          _emailController.text, _passwordController.text);
      print(userCredential?.user.uid);
      // 3
      if (userCredential != null) {
        firebaseAuth?.updateUserInformation(userCredential.user.uid,
            userCredential.user.idToken!, {'role': _roleController.text});
      }

      setState(() {
        _status =
            "User created successfully with role: ${_roleController.text}";
      });
    } catch (e) {
      setState(() {
        _status = "Failed to create user: $e";
      });
    }
  }

…

In the above code:

  1. First, we create an instance of the SDK and initialize the same in the initState of the widget.
  2. Later in the registerNewUser  we are first creating the user using the email and password with firebaseAuth which returns us an UserCredential object.
  3. And finally we check whether userCredtial is not null and update the user with the role.

Now, we can utilize the above method on the Create user button as below:

ElevatedButton(
              onPressed: registerNewUser,
              child: const Text('Create User'),
            )

N.B., Make sure Email/Password Sign-In Provider is enabled.

User Registration

Verifying the User

Post creating the user, verify in your Firebase console for the new user:

Finally, a user is created using Firebase Dart Admin Auth SDK and before we wrap up, have a look at some common issues and solutions.

Common Issues and Solutions

  • Key Exposure: Ensure your Service Account Key is never included in public repositories by adding it to .gitignore.
  • Permission Errors: Make sure your Firebase Service Account has sufficient permissions to perform the required tasks, like managing users or sending notifications.

Conclusion

Integrating Firebase Service Account Keys with Flutter allows you to automate backend operations securely and efficiently. With the Firebase Dart Admin Auth SDK, you can manage users, perform server-side tasks, and streamline backend processes without the need for user interaction. 

This approach not only enhances security but also simplifies development for tasks like user management. By following best practices for securing your keys and ensuring proper permissions, you can safely leverage Firebase’s powerful features in your Flutter applications.

For the full code examples, check out my GitHub repository

About the author
Himanshu Sharma

Himanshu Sharma

Software engineer skilled in scalable data pipelines, ETL processes, and backend optimization. Passionate about Flutter, open source, and sharing insights with the developer community

Dart Code Labs

Thoughts, stories and ideas.

Dart Code Labs

Great! You’ve successfully signed up.

Welcome back! You've successfully signed in.

You've successfully subscribed to Dart Code Labs.

Success! Check your email for magic link to sign-in.

Success! Your billing info has been updated.

Your billing was not updated.