App Signing and Firebase Authentication Issues

Siddhartha Mishra
7 min readMay 26, 2024

Introduction

So, you are starting to take your app out of the emulator and run it on actual devices instead of doing “Flutter run” and running it on a test device. Oh wow, so you also have Firebase baked into your app and you think you understand how it all works. I’ll try and help you through this mess. I’m still not a complete expert so there might be things that I may get wrong so, feel free to send back constructive criticism and feedback.

Go through the billing thoroughly. Don’t make $8000 bill the first day.

build.gradle (./android/app)

This is what your current build.gradle file will be looking like:

android {
compileSdkVersion flutter.compileSdkVersion
ndkVersion flutter.ndkVersion

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
}

kotlinOptions {
jvmTarget = '1.8'
}

sourceSets {
main.java.srcDirs += 'src/main/kotlin'
}

defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "com.example.area_51"
// You can update the following values to match your application needs.
// For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration.
minSdkVersion 19
targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger()
versionName flutterVersionName
}

buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}
}

This is a debug gradle. Understand. The following code is processed when you go Flutter run in the terminal. It signs the app with what is called a debug keystore.

buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}

But wait, what is signing an app actually?

Signing Application

While compiling an application is signed using two SHA keys:- SHA-256 and SHA-1. These keys are verification that the application belongs to the user that made the application. I will only mention there use to be while setting up Google Auth and also while publishing the app on Play Store, as these are the only uses I have encountered till now. I’ll better explain how it works but first, let’s make our keystores which store the SHA keys we need.

Creating a keystore file

1. debug.keystore

Firstly, if you delete your keystore file in C:/Users/$USERNAME$/.android/ while running an app in debug mode, a new keystore will be automatically created by Flutter which will be used to sign all apps that are run in debug mode on your computer. But if you want a custom debug.keystore file:

 keytool -genkey -v -keystore debug.keystore -storepass android -alias androiddebugkey -keypass android -keyalg RSA -keysize 2048 -validity 10000

But well you get this automatically too so not a very big deal. Though, since this is for authentication, you must know what the keys are. The keys however, cannot be accessed without knowing a few parameters and running the following command

keytool -list -v -keystore debug.keystore -alias androiddebugkey -storepass android -keypass android 

This command can be used to view keystore or .jks files and check out the keys are being used to sign the app but for that you must know the storepass and keypass.

These can only be known to the app creator and hence, the authentication is successful. I’ll explain the process in detail but will first finish up with the types of keys used to sign an app.

2. upload-keystore.jks

This is a release keystore. Whenever you build a release apk or an app bundle the application is signed using the release keystore.

Do remember the keyPassword, alias and storePassword that you set.

keytool -genkey -v -keystore upload-keystore.jks -keyalg RSA -keysize 2048 -validity 10000 -alias upload

This will create a jks file containing the SHA-256 and SHA-1 keys to sign the release app, in the folder where your terminal is when you run the command. Move the keystore file to the flutter app, placing it in ./android/app.

-Project
-android
-app
-src
-build.gradle
-upload-keystore.jks //ADD THE JKS FILE HERE
-gradle
-assets
-ios
-build
-lib
-linux
-macos
-test
-web
-windows

You have provided the jks file to the build.gradle but you haven’t provided the build.gradle the necessary parameters to use the upload-keystore. You can check the SHA keys using the keytool -list command which I mentioned above. So, setting up these keys for release signing requires a few more steps.

  1. Creating a key.properties file — Place it in ./android folder
storePassword=<password that you set>
keyPassword=<password that you set>
keyAlias=upload
storeFile=/upload-keystore.jks <RELATIVE PATH FROM KEYSTORE FILE TO JKS>

Now that you have all the necessary credentials stored up, we move on to adding commands to build.grade to sign the release app using these params.

2. Modifying build.gradle

Remember we had the following code while debugging? It basically uses the debug.keystore to sign the application. Even the default comments ask us to add our own signing config when we are building the release version for the app.

buildTypes {
release {
// TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works.
signingConfig signingConfigs.debug
}
}

But now we need to sign the app differently for the release version. So, we make a few chances to the build file.

def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android{
...

signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}

buildTypes {
release {
signingConfig signingConfigs.release // CHANGED TO USE CONFIGURATION PROVIDED FOR RELEASE SIGNING
}
}
}

So when you run

flutter build appbundle

or

flutter build apk

You create a release build which is signed by the SHA keys in upload-keystore.jks. There’s one more set of keys that are used to signed an application and that is

3. Google Play Store App Signing

Basically when you wish to publish to PlayStore, Google offers to create a signing key which automatically signs the app everytime you publish a new bundle(app version) to Play Store.

Press — Choose signing Key

This will allow you to have the following options:

Now, if you go with the recommended option (which I did) you’ll have a third type of signing key that you’ll need to manage with your app.

Test out with the other three options, I guess there’ll be an alternative where you can work with the release upload-keystore.jks that you already created.

Firebase Integration

Go to your project settings in your Firebase project console where you can see all your apps. Now, if you have added one that is like — com.example.appname then I guess you’ll have to do one more step.

Changing app package name

Skip this if you don’t need it.

Guys don’t use example keyword in your package name, it’ll cause issues when publishing — to be exact Google won’t allow you to publish. So, basic steps to change this in your app will be running the following:

pub global activate rename
pub global run rename --bundleId <new package name> --target android

Yeah moving on.

Firebase Integration (continued)

You have the new app set up. Now, we gotta add the SHA keys to the app. Below the section for SHA certificate fingerprints you’ll get the option to Add Fingerprint.

What you have to do is add ALL SHA keys that are necessary to your project such as the debug keys, the release keys and the Google Play Store App Signing keys. Only then shall it function correctly.

Download and add the google-service.json to your ./android folder. It’ll look something like this. Now, here the certificate_hash will be the Google Play Store publishing SHA-1 key for the verification to work when you publish the app to Play Store.

{
...

"client": [
{
"client_info": {
"mobilesdk_app_id": "<id>",
"android_client_info": {
"package_name": "com.<package>",
"certificate_hash": "<hash>"
}
},
"oauth_client": [],
"api_key": [
{
"current_key": "<key>"
}
],
"services": {
"appinvite_service": {
"other_platform_oauth_client": []
}
}
}
],

...
}

At this point in time, I am not sure whether verifications will run in debug and release builds of the app but I will make sure to check and update it here. Now, for the final part of this-

How Does It Work?

First, debug keys

  1. Debug keys are mostly auto-generated so you basically go:
flutter run

And debug keys are generated by the Flutter engine, which signs the app with the same and stores it in .android which mostly is present in C:/Users/$USERNAME$/.android.

2. The app communicates the debug keys to Firebase through google-services.json file and if the keys are present in the Firebase app’s list of SHA fingerprints, it confirms verification.

For release keys,

  1. The build.gradle accesses the upload-keystore.jks through the key.properties folder and signs the app as you have specified to be signed for release using release params for signingConfigs.
def keystoreProperties = new Properties()
def keystorePropertiesFile = rootProject.file('key.properties')
if (keystorePropertiesFile.exists()) {
keystoreProperties.load(new FileInputStream(keystorePropertiesFile))
}

android{
...

signingConfigs {
release {
keyAlias keystoreProperties['keyAlias']
keyPassword keystoreProperties['keyPassword']
storeFile keystoreProperties['storeFile'] ? file(keystoreProperties['storeFile']) : null
storePassword keystoreProperties['storePassword']
}
}

buildTypes {
release {
signingConfig signingConfigs.release // CHANGED TO USE CONFIGURATION PROVIDED FOR RELEASE SIGNING
}
}
}

2. And then the verification

Finally for Google Play Store also, when you upload the appbundle Google signs it using the keys specified to it and builds the final appbundle, ready for publishing.

Conclusion

There might be inconsistencies and things I missed and I apologize for that. I’m still learning and all this I figure out on my own going through hours and hours of documentations, videos, StackOverflow threads, Github issue threads and what-not. I hope this helps you and if you have any additions, modifications and fixed or even questions feel free to reach out and I’ll try and help you as well as I can :)

Peace. Happy coding!

--

--