20

I am using googleapis services in my flutter app which requires some credentials in JSON format. What is the best way to store this credentials in my App?

Can I keep a JSON file in my asset folder and read it in my main function?

Or should I hardcode the credentials in my main function? I'm new to flutter development.

My code looks like the following

import 'package:googleapis/storage/v1.dart';
import 'package:googleapis_auth/auth_io.dart';

final _credentials = new ServiceAccountCredentials.fromJson(r'''
{
  "private_key_id": ...,
  "private_key": ...,
  "client_email": ...,
  "client_id": ...,
  "type": "service_account"
}
''');

const _SCOPES = const [StorageApi.DevstorageReadOnlyScope];

void main() {
  clientViaServiceAccount(_credentials, _SCOPES).then((http_client) {
    var storage = new StorageApi(http_client);
    storage.buckets.list('dart-on-cloud').then((buckets) {
      print("Received ${buckets.items.length} bucket names:");
      for (var file in buckets.items) {
        print(file.name);
      }
    });
  });
}

Where I should keep the following credentials:

{
  "private_key_id": ...,
  "private_key": ...,
  "client_email": ...,
  "client_id": ...,
  "type": "service_account"
}

I don't think hardcoding like above is a good idea.

I think this should work:https://medium.com/@sokrato/storing-your-secret-keys-in-flutter-c0b9af1c0f69

Thanks.

Unnikrishnan
  • 2,683
  • 5
  • 22
  • 39

3 Answers3

29

For storing sensitive information like credentials you should use the Keychain unter iOS and the Keystore under Android.

There is a perfect library for that called flutter_secure_storage.

Here's how to use that:

// Create storage
final storage = new FlutterSecureStorage();

// Store password 
await storage.write(key: "password", value: "my-secret-password");

// Read value 
String myPassword = await storage.read(key: "password");

To use it add flutter_secure_storage: 3.2.1+1 to you pubspec.yaml and run flutter packages get in a terminal.

Here is the package and a more detailled example on how to use it: https://pub.dartlang.org/packages/flutter_secure_storage

Robin Reiter
  • 2,281
  • 15
  • 24
  • 12
    This is API credentials like an API key. This is not something I'm getting from the user. Where I should keep my api key? – Unnikrishnan Apr 13 '19 at 08:11
  • 5
    That is basically a non-Flutter related issue. Storing API keys as a constant string somewhere in your code is always easily detectable if a hacker has access to your binary. There are multiple ways in tackling such an issue. For example you could implement some sort of https login system with a username and a password. Your API may then generate a Key which you can store securely. But that is just one way of doing that. – Robin Reiter Apr 13 '19 at 10:38
  • 3
    @RobinReiter What about API keys for Authentication? Chicken-egg? – AWhitford Apr 23 '19 at 07:48
  • 10
    This doesn't answer the question, he needs a way to store the API KEY without hardcoding it – Jorge Luis Nov 08 '20 at 05:22
18

Comment: I dont see how Robin actually answers the question. The original poster needs the application to have credentials to access an API. Using secure key storage does not give the application any data so it still can't access the API. If you add the file in your project, then secure key storage does not secure the file in any way.


2 approaches

Environment variables: I suggest using environment variables for variables that change depending on the environment: you might have 2 API_KEYS because you have a production and testing environment. However, attackers with your app can still steal this by just 'man-in-the-middling'-ing your requests, for example, using proxyman.A ttackers can sniff traffic or decompile your application to steal API_KEYS and use them in their own projects, exploits or cryptomining. Therefore, if you’re using environment variables, ensure your API keys are not that powerful. Some API providers will allow you to limit the power of that key.

Backend: So, if the API_KEY is powerful and unrestricted, then it should not be used on the application at all. It should be in your backend, which calls external APIs on behalf of clients. You could host a server to do this, or use serverless functions (e.g. AWS Lambda, GCP cloud functions, Azure functions, Firebase cloud functions, and lots more).


An example

It depends on your threat model: what kind of threats you think is possible against your app. In my case, I'm not that bothered about someone decompiling my hackathon project to steal a weather API_KEY that I got for free, but I am afraid that some github crawlers or the api provider finding this api_key and disabling it (This happened to my friend with her Google Cloud Platform service account credentials). So, I am using environment variables.

Ben Butterworth
  • 22,056
  • 10
  • 114
  • 167
  • 1
    Environment variables doesn't hide anything in case of Flutter apps. The secret value is still ends up being a part of the application code (just like with SPA applications). The only right and secure way is what you suggest in the "Backend" part of your response. – Igor Soloydenko Feb 02 '21 at 04:19
  • The environment variables will always be on the device, not just in Flutter, so Flutter is not extra dangerous in this sense. I added a small bit to say, if you use Environment variables, ensure they’re locked down. – Ben Butterworth Feb 08 '21 at 06:35
  • Yes, it not "extra" dangerous indeed. I am saying, it's never safe to do that in the first place. – Igor Soloydenko Feb 08 '21 at 18:30
  • Robin actually answered the question perfectly. Most auth systems will have you request an API key using your credentials which you may then store on keychain/keystore for further use. After the TTL of the key is up, your app asks you to re-authenticate - this is a very common use-case. OP should have users authenticate to Google services first with their username/pwd and then handle storing the API key that it'll respond with. – ravenblizzard Sep 03 '21 at 10:31
  • 1
    No @ravenblizzard, doing `await storage.write(key: "password", value: "my-secret-password");` is just plain wrong, those are hardcoded credentials. In the case where credentials are passed to the device from a server, sure you could use `flutter_secure_storage`, but neither the question nor Robin's answer mention credentials are passed from the server, in the slightest. Thanks for the downvote. – Ben Butterworth Sep 03 '21 at 10:37
  • @BenButterworth His code snippet is clearly an example and he's suggesting that OP should use secure storage to store users' credentials (API key or whatever). You suggest that he save his API key as environment variables, are you saying that's better than using the keychain? – ravenblizzard Sep 03 '21 at 11:23
2
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
/*  
* Example of a secure store as a Mixin 
* Usage: 

import '../mixins/secure_store_mixin.dart';

MyClass extends StatelessWidget with SecureStoreMixin {

  exampleSet(){
    setSecureStore('jwt', 'jwt-token-data');
  }

  exampleGet(){
     getSecureStore('jwt', (token) { print(token); });
  }
}

*/

class SecureStoreMixin{

  final secureStore = new FlutterSecureStorage();

  void setSecureStore(String key, String data) async {
    await secureStore.write(key: key, value: data);
  }

  void getSecureStore(String key, Function callback) async {
    await secureStore.read(key: key).then(callback);
  }

}

Note: Extend by adding more methods:

  • get all: Map<String, String> allValues = await secureStore.readAll();
  • delete: await secureStore.delete(key: key);
  • delete all: await secureStore.deleteAll();
Mods Vs Rockers
  • 1,031
  • 8
  • 6