Engineering

How to securely use Google Maps API Key in Android, iOS, or Flutter Project

When you’re building an Android, iOS, or Flutter Application, it’s important to keep your API keys secure. This means that API keys should not be checked into your version control tool. But still, you need the ability to reference them in your local environment as well as on CI.

In this article, I’m assuming that on CI, keys are stored as environment variables on the machine you’re building your project. I’m skipping this part because there are plenty of ways to set environment variables. I want to focus on how to make a reference in the code to the keys on both: local machine and CI.

Android

According to Google Documentation to store API keys securely it is recommended to use local properties. If you don’t have the file local.properties in your project, it should be located in the android project root directory. Make sure to add it to intentionally untracked files in your version control tool. Now you can add here a new variable. Note that the value of the key should NOT be wrapped by either single or double quote characters.

googleMapsApiKey=YOUR_GOOGLE_MAPS_API_KEY_STRING_VALUE

The next step is to define the variable in the Gradle file build.gradle located in the \app directory to make it work on local and CI machines:

def localProperties = new Properties()
localProperties.load(new FileInputStream(rootProject.file("local.properties")))
def googleMapsApiKey = localProperties.getProperty('googleMapsApiKey')
if (googleMapsApiKey == null) {
   googleMapsApiKey = System.getenv("GOOGLE_MAPS_API_KEY_ENVIRONMENT_VARIABLE")
}

And add it to manifestPlaceholders in the defaultConfig:

manifestPlaceholders = [
       googleMapsApiKey       : googleMapsApiKey
]

And that’s it. Now you can refer to the key in the AndroidManifest.xml.

<meta-data
   android:name="com.google.android.geo.API_KEY"
   android:value="${googleMapsApiKey}" />

iOS

For iOS, I will use Xcode build configuration files. This approach requires two config files Release.xcconfig and Debug.xcconfig in the project directory.

For the iOS project, you can use Xcode to create the configuration file by “File > New > File…” and finding in section “other” Configuration Setting File.

For the Flutter project, you can simply add these files in the ios\Flutter directory.

Make sure to add the Debug.xcconfig file to intentionally untracked files in your version control tool.

For Release.xcconfig it needs to refer to the environment variable:

GOOGLE_MAPS_API_KEY=$(GOOGLE_MAPS_API_KEY_ENVIRONMENT_VARIABLE)

And for Debug.xcconfig set the actual key. Note that the value declared key should NOT be wrapped by either single or double quote characters.

GOOGLE_MAPS_API_KEY=YOUR_GOOGLE_MAPS_API_KEY_STRING_VALUE

Next, in the Info.plist refer to the variable in the configuration file:

<key>googleMapsApiKey</key>
<string>$(GOOGLE_MAPS_API_KEY)</string>

and finally, in the AppDelegate.swift it’s possible to refer to the variable:

GMSServices.provideAPIKey(Bundle.main.object(forInfoDictionaryKey:"googleMapsApiKey") as? String ?? "");

Summary

Of course, everyone should remember that at end of the day API Keys will be somewhere in the binary code of our application. Defending ourselves from people who know what they are doing is impossible but at least it’s possible to make their lives a little bit harder. AndroidManifest.xml and Info.plist files are relatively easy to convert from binary to text. There are alternatives to the proposed solutions like Cocoapods-Keys or worst case scenario keeping keys in source code :)

It is worth reminding the fact that usually the same key is used for both Android and iOS Applications. It’s important to keep at least a minimum amount of security on both sides because it doesn’t matter which one will leak a key.

Additional knowledge base

For more information, you can check out the documentation for adding keys to the Android project for setting up project Android project and for setting up an iOS project. I also recommend reading about secret management on iOS. If you are interested in understanding Apple binaries, take a look at this article on Medium, which casts a little bit more light on the subject.

Update: In Flutter 2.5.3 there is a new feature added called --dart-define which allows to setup keys and secrets for Android and iOS directly while building a project. More about it you can read in this article.