I am working on a fitness app using flutter. I am attempting to use Fatsecret API for food and recipe database.
I am new to flutter and API's in general (previously as a junior android developer, only worked with firebase). Now I am stuck at generating OAuth signature for fatsecret API.
Fatsecret documentation for signature generation, but i dont understand it.
This is my code
import 'dart:convert';
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
import 'package:http/http.dart' as http;
import 'package:random_string/random_string.dart';
import 'package:sortedmap/sortedmap.dart';
// FatSecret API
class FsApiService {
/// I used these tuts for reference
// https://blog.dantup.com/2017/01/simplest-dart-code-to-post-a-tweet-
// using-oauth/
// http://platform.fatsecret.com/api/Default.aspx?screen=rapiauth
// https://github.com/EugeneHoran/Android-FatSecret-REST-API
// https://stackoverflow.com/questions/49797558/how-to-make-http-post-
// request-with-url-encoded-body-in-flutter
//https://groups.google.com/a/dartlang.org/forum/#!topic/cloud/Ci1gFhYBSDQ
// https://stackoverflow.com/questions/28910178/calculating-an-oauth-
// signature
static const API_KEY = 'API_KEY_HERE';
static const SHARED_SECRET = 'SHARED_SECRET_HERE';
static const APP_METHOD = 'POST';
static const REQUEST_URL =
'http://platform.fatsecret.com/rest/server.api';
static const SIGNATURE_METHOD = 'HMAC-SHA1';
static const OAUTH_VERSION = '1.0';
var _sigHasher;
FsApiService() {
var bytes = utf8.encode('$SHARED_SECRET&');
_sigHasher = new Hmac(sha1, bytes);
}
/// Fetches all foods from Fatsecret Api
fetchAllFoodsFromApi() async {
Map<String, String> params = {
'oauth_consumer_key': API_KEY,
'oauth_signature_method': SIGNATURE_METHOD,
'oauth_timestamp':
(DateTime.now().millisecondsSinceEpoch).toString(),
'oauth_nonce': nounce(),
'oauth_version': (1.0).toString(),
'format': 'json',
'method': 'foods.search',
'search_expression': 'cheese'
};
var signatureUri = _generateSignature(APP_METHOD, REQUEST_URL, params);
params['oauth_signature'] = signatureUri;
var sortedParams = SortedMap.from(params);
var client = http.Client();
final response = await client.post(
REQUEST_URL,
headers: sortedParams,
);
print(response.statusCode);
print(response.body);
print('$signatureUri');
print('$sortedParams');
print('$params');
}
String nonce() {
return randomString(8);
}
String _generateSignature(
String method, String baseUrl, Map<String, String> params) {
var encodedMethod = Uri.encodeComponent(method);
var encodedUrl = Uri.encodeComponent(baseUrl);
var sortedParams = SortedMap.from(params);
var concatedParams = _toQueryString(sortedParams);
var encodedParams = Uri.encodeComponent(concatedParams);
var finalUrl = '$encodedMethod&${_encode(encodedUrl.toString())}'
+ '&${_encode(encodedParams)}';
var base64converted = base64.encode(_hash(finalUrl));
print('encoded method = $encodedMethod');
print('encoded url = $encodedUrl');
print('encoded params = $encodedParams');
print('final url = $finalUrl');
print('base64converted = $base64converted');
return base64converted;
}
String _toQueryString(Map<String, String> data) {
var items = data.keys.map((k) => "$k=${_encode(data[k])}").toList();
items.sort();
return items.join('&');
}
String _encode(String data) {
return percent.encode(data.codeUnits);
}
List<int> _hash(String data) => _sigHasher.convert(data.codeUnits).bytes;
}
When I run the app, following error message is shown in logcat
2018-11-01 19:53:17.681 25882-25907/com.example.ninjaapp I/flutter: 200
2018-11-01 19:53:17.735 25882-25907/com.example.ninjaapp I/flutter: <?xml
version="1.0" encoding="utf-8" ?>
<error xmlns="http://platform.fatsecret.com/api/1.0/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://platform.fatsecret.com/api/1.0/
http://platform.fatsecret.com/api/1.0/fatsecret.xsd">
<code>2</code>
<message>Missing required oauth parameter:
oauth_signature_method</message>
</error>
am I creating the signature in an incorrect way? Or can anyone see where I am going wrong?
Thank You!