Is there a safe way to manage API keys?
Asked Answered
H

4

51

I am using an API within my app. I currently manage the API key from a java interface

public interface APIContract {
    //The API KEY MUST NOT BE PUBLISH. It is possible to generate a new one for free from www.themoviedb.org
    //Remove before commit !!!
    String API_KEY = "abcdefghijklmnopqrstuvwxyz";
    /...
}

This do the job. I can access the key using APIContract.API_KEY, but as you can see in the comment this is not safe if I use git and a public repository (I am not suppose to publish this key).

So here is my question : is it possible to move this key in another place which I can easily access from my app but which will not be committed ?

I found this thread which use gradle to store the key, but I need to commit the build.gradle file so it does not do the job.

Does someone know how to solve this problem ? I did not find similar problem in stackoverflow but maybe I missed something

EDIT I love the idea of moving the key outside any java code because other people (maybe non technical people) can easily manage their own key. I was thinking about using a gradle file like settings.gradle.

Humo answered 14/10, 2015 at 19:31 Comment(4)
What if you put in a separate (static) class, remove the key, commit, add key and add "do not track" on the class?Lapidary
Sure but it will be tricky for external people to manage their keys (see edit). I was thinking of something really accessible and easy to modify (like in the root directory)Humo
Why would it be tricky? They just add that key once and that's it.Lapidary
Possible duplicate of Best Practice for storing private API keys in AndroidDissenter
P
28

Here is another way:

Place the API key in a file accessible to the build machine/server, we'll call it:

/usr/api_user/api_key1

With contents:

myApiKey = abcdefghijklmnopqrstuvwxyz

You will now access it using the `BuildConfig' gradle object. Modify your code to this:

public interface APIContract {
    //The API KEY MUST NOT BE PUBLISH. It is possible to generate a new one for free from www.themoviedb.org
    //Remove before commit !!!
    String API_KEY = BuildConfig.MY_API_KEY;
    /...
}

Then in your build.gradle, add something like this:

buildConfigField "String", "MY_API_KEY", getMyApiKey("myApiKey")

And also add this:

//return a MY API KEY from a properties file.
def getMyApiKey(String property){
    Properties properties = new Properties()
    properties.load(new FileInputStream("/usr/api_user/api_key1"))
    return "\"" + properties.getProperty(property) +"\""
}

You can relocate the API directory location, as you can tell, so that it is not a part of your repo. Of course, then it will have file system dependencies for the build... which you could have a list setup in a CI/CD environment (maybe a tool like Jenkins) to replicate the build files to a private repo, for backup purposes.

Placate answered 14/10, 2015 at 21:40 Comment(2)
Should I define the def getMyApiKey(String property) function in the duild.gradle ?Humo
yes - the "buildConfigField should not be in the same block - it should be where you build your app in the "Android" blockPlacate
R
63

1. Store api keys in an xml file

Put xml file "api_keys.xml" in the directory "res/values/".

api_keys.xml

<?xml version="1.0" encoding="utf-8"?>
<resources>
    <string name="THE_MOVIE_DB_API_TOKEN">XXXXX</string>
</resources>

use api keys in java code

context.getString(R.string.THE_MOVIE_DB_API_TOKEN);

2. Store API keys with help of Gradle and the gradle.properties file

Example_0 Example_1

Add the following line to [USER_HOME]/.gradle/gradle.properties

For Windows OS, an example for Denis user:

C:\Users\Denis\.gradle

gradle.properties

MyTheMovieDBApiToken="XXXXX"

Add the following code to the build.gradle file

build.gradle

apply plugin: 'com.android.application'

android {
    ...

    defaultConfig {
        ...
    }
    buildTypes {
        release {
            ...
        }
    }
    buildTypes.each {
      it.buildConfigField 'String', 'THE_MOVIE_DB_API_TOKEN', MyTheMovieDBApiToken
    }
}

use api keys in java code

BuildConfig.THE_MOVIE_DB_API_TOKEN)

3. Store API keys with help of gradle and the system path variable

Example_0

Add new system PATH variable THE_MOVIE_DB_API_TOKEN="XXXXX":

For Windows OS:

  • open system
  • advanced system settings
  • environment variables
  • add new variables to the user variables

Add the following code to the build.gradle file

build.gradle

apply plugin: 'com.android.application'

android {
    ...

    defaultConfig {
        ...
    }
    buildTypes {
        release {
            ...
        }
        buildTypes.each {
            it.buildConfigField 'String', 'THE_MOVIE_DB_API_TOKEN', "\"$System.env.THE_MOVIE_DB_API_TOKEN\""
        }
    }
}

use API keys in java code

BuildConfig.THE_MOVIE_DB_API_TOKEN

Link to my gist on github

Retrieval answered 1/12, 2015 at 13:21 Comment(3)
Would you be able to please explain how storing the key within the gradle.properties file essentially hides the values? I'd imagine that I'd still need to explicitly not include this file within my .gitignore file (assuming that I'm using Git). Is that correct? I will also note that I have minimal understanding of Gradle and how all of the files are related. Any insight is appreciated!Maxia
@Maxia yes, if your gradle.properties file will be stored in your app's repository you would include this file in your .gitignore. In this post it describes storing the properties on your local machine's .gradle folder in the user directory.Cogitative
Note - You may have to rebuild project to access BuildConfig.THE_MOVIE_DB_API_TOKENSublime
P
28

Here is another way:

Place the API key in a file accessible to the build machine/server, we'll call it:

/usr/api_user/api_key1

With contents:

myApiKey = abcdefghijklmnopqrstuvwxyz

You will now access it using the `BuildConfig' gradle object. Modify your code to this:

public interface APIContract {
    //The API KEY MUST NOT BE PUBLISH. It is possible to generate a new one for free from www.themoviedb.org
    //Remove before commit !!!
    String API_KEY = BuildConfig.MY_API_KEY;
    /...
}

Then in your build.gradle, add something like this:

buildConfigField "String", "MY_API_KEY", getMyApiKey("myApiKey")

And also add this:

//return a MY API KEY from a properties file.
def getMyApiKey(String property){
    Properties properties = new Properties()
    properties.load(new FileInputStream("/usr/api_user/api_key1"))
    return "\"" + properties.getProperty(property) +"\""
}

You can relocate the API directory location, as you can tell, so that it is not a part of your repo. Of course, then it will have file system dependencies for the build... which you could have a list setup in a CI/CD environment (maybe a tool like Jenkins) to replicate the build files to a private repo, for backup purposes.

Placate answered 14/10, 2015 at 21:40 Comment(2)
Should I define the def getMyApiKey(String property) function in the duild.gradle ?Humo
yes - the "buildConfigField should not be in the same block - it should be where you build your app in the "Android" blockPlacate
A
1

You can add the key to the gradle.properties file or pass it as an argument

Austronesian answered 14/10, 2015 at 19:50 Comment(2)
But the gradle.properties file can be commit. I can add it to .gitignore but I will not be able to use it to store other propertiesHumo
Then pass it as an argumentAustronesian
N
1

I put them on the global gradle.properties file on the dev machine or ci server. This way is not part of your project so you shouldn't worry about checking it in your repo, but it's still easily accessible from your gradle files. Global gradle.properties file should be located inside ˜/.gradle on Mac for instance - btw, you should create it if it doesn't exist. Then on your build.gradle file you can reference those properties and, for instance, expose them as buildConfigFields using the same name you used in your gradle.properties file

buildConfigField 'String', 'API_KEY', maps.api.key

Then on your Java/Kotlin code you can access them as BuildConfig.API_KEY

Northeasterly answered 22/6, 2018 at 12:27 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.