Philip Mutua
Philip Mutua

Reputation: 6881

Correct way of storing API Keys in flutter following best practises

Which is the correct way(best practice) of adding secret API keys in flutter in case I want to push the code on github. I've made a simple app that consumes an API but I used the key in a crud way just to test whether the app is working. Usually from my experience developing applications in the back-end, Keys are stored somewhere and in different file then one would simply import it to the required file that needs the API_KEY and exclude the file in .gitignore file.

So far I have also implemented this approach:

Folder tree

-lib
  -auth
    -keys.dart
    -secrets.json 

secrets.json

This is where I will add the KEY and specify this file in .gitignore to be excluded from being added in github when I push my code.

//Add API KEY HERE
{
  "api_key": "ee4444444a095fc613c5189b2"
}

keys.dart

import 'dart:async' show Future;
import 'dart:convert' show json;
import 'package:flutter/services.dart' show rootBundle;


class Secret {
  final String apikey;

  Secret({this.apikey=""});

  factory Secret.fromJson(Map<String, dynamic>jsonMap){
    return new Secret(apikey:jsonMap["api_key"]);
  }
}


class SecretLoader {
  final String secretPath;

  SecretLoader({this.secretPath});
  Future<Secret> load() {
    return rootBundle.loadStructuredData<Secret>(this.secretPath,
            (jsonStr) async {
          final secret = Secret.fromJson(json.decode(jsonStr));
          return secret;

        });
  }
}

I feel like this approach is too much. I would like to get suggestions of a better approach.

Upvotes: 89

Views: 59673

Answers (10)

thenish
thenish

Reputation: 77

we should not store secret keys in a file because it is easy to decompile. You can try these methods

  • Fetch it from another source like firebase remote config
  • Hardcode key in a string with private accessible
  • Encrypt your key and store in a string and decrypt it whenever you need, don't store the decrypted key anywhere, just use getter function.

Upvotes: -1

Sanjay Bharwani
Sanjay Bharwani

Reputation: 4749

We are using the Flutter DotEnv package for this.

Include this package in pubspec.yaml

flutter_dotenv: ^5.1.0

Create a file named .env in root folder. Register this .env file as a asset

  assets:
    - .env

Add loading of dotenv in your main function before loading the app void main() async { await dotenv.load(); }

Add the .env file in .gitignore file so it shouldn't be commited

Now add the required keys in the .env file, for e.g.

dev_firebase_remote_config_api_key=apikey_124433
sit_firebase_remote_config_api_key=apikey_1243734
uat_firebase_remote_config_api_key=apikey_1248262

These env config can be read in the code using.

import 'package:flutter_dotenv/flutter_dotenv.dart';

and now read the config

dotenv.env['dev_firebase_remote_config_api_key']!

Please refer Flutter DotEnv for more details

Upvotes: 0

abarraford
abarraford

Reputation: 734

You should never put API keys into your client apps (unless they are marked as public keys), as mentioned above they are now compromised and could be used in malicious context. Additionally, it's generally a good idea to be able to roll-over api-keys, secret-keys periodically. Embedding them into your client apps just about kills that notion without some complex mechanism that in-itself will be an anti-pattern.

What we should be doing:

  1. Have an Authentication flow, where your client side inherits an identity of the end user, be it a custom email auth, sign-in with Provider auth (Google, Facebook)...
  2. If using a Provider, like Google Oauth2, don't put your Provider secret key in the client app (tutorials on this are generally not great. Only embed keys when the provider explicitly says you can), the code<=>token exchange should occur on the server-side when the secret key is necessary, or when you want to validate id_tokens.
  3. Send back to your client a Bearer token that represents their identity. This is all to limit scope of what secret is exposed to the end user. Bearer tokens must not contain sensitive information and must be signed (if your going to send back your own).
  4. Create a server-side implementation(s) for negotiation between API's and your Clients. Your Client app call your back-end API with their Identity Token, your back-end service call the API with secret and pass through results back to the client app.

As far as the backend, server-side impl. It can be as simple as a server-less function such as an AWS Lambda + API Gateway (1Million executions a month for free from Lambda). Or you can host larger, more complex service backends if needed.

Upvotes: 1

osw
osw

Reputation: 1

A way of storing API Keys safely in flutter local repository from not being uploaded to a remote repository, is to use dart package flutter_dotenv, here I put the link of the package with all the information about it link to package

Upvotes: 0

Noodles
Noodles

Reputation: 4080

EDIT: Look at Sludge's comment below.

EDIT 2: The issue in described at the bottom has been fixed in firebase-config 19.0.2.

Use Firebase Remote Config. Inside the Firebase console, inside the menu, scroll down to Grow and then Remote Config. Here you can add a parameter with a value. When you're done don't forget to publish the changes. It's kind of subtle.

enter image description here

Now install firebase_remote_config for Flutter.

After importing everything, you can retrieve your value using this code:

RemoteConfig remoteConfig = await RemoteConfig.instance;
await remoteConfig.fetch(expiration: Duration(hours: 1));
await remoteConfig.activateFetched();

remoteConfig.getValue('key').asString();

This way, the API key or token is never part of your application.

Note: there is currently an issue where you get a warning stating the application's name is not set, but this won't affect functionality.

Upvotes: 7

stephen meckstroth
stephen meckstroth

Reputation: 345

The best practice should be to create a Firebase function, and then have that function use the secrets storage. This approach keeps the secrets out of your source code so there are no issues with sharing the code, and stores them securely. Firebase authenticates your app (and users if you use the login feature).

Secret Parameters

Call functions from your app

Upvotes: 3

genericUser
genericUser

Reputation: 7118

API Keys must be hard coded.

Why?

Because it's impossible to retrieve the key from an external source without having a key to that source, and you can't store it securely at the OS level without having the key within the app.

Example

When you connect your app to a Firebase project or Google Cloud servers, you basically authenticate using a hard-coded API key, that you have downloaded into your app when initiating your cloud project (read more).

Security

There are two essential steps to secure your critical assets:

  1. Keep your secret keys out of version control.
  2. Use obfuscation to make it difficult for attackers to reverse engineer your application, and reveal your API Key.

IMO, those are the only steps you can take to secure your app API Key.

Upvotes: 27

&#193;lvaro Ag&#252;ero
&#193;lvaro Ag&#252;ero

Reputation: 4800

You can use flutter_secure_storage from the oficial Flutter Packages

Upvotes: -4

Rolando Urquiza
Rolando Urquiza

Reputation: 1450

For secure storage you have to rely on the corresponding native platforms, both iOs and Android provide a mechanism to securely store keys. You can implement it by yourself and use the flutter channels to obtain and store the keys. Information about this mechanism can be read here:

Android Keystore

iOs KeyChain

Also, you can use this flutter plugin, which uses the services mentioned above and provides a dart object to access the secure storage.

Upvotes: 8

Gainz
Gainz

Reputation: 1799

As mentioned, if the key is a secrete and you would like to protect it then simply do not put it in the client app. The app can be de-compiled and the key can be extracted for person willing to target your client.

I would delegate the task of communicating with this API to your Application Server. You can put the key in your server and have your server communicate with this external API and relay the response to the client.

Edit: Another approach, which is less secure but more convenient is to obfuscate your code using something like proguard. See this page for flutter instruction on android app: https://flutter.io/android-release/

Upvotes: 5

Related Questions