Hello, has anyone succeeded in authenticating oauth 1.0 signature? I'm coding with dart

241 views
Skip to first unread message

김도연

unread,
May 26, 2023, 9:20:57 PM5/26/23
to FatSecret Platform API
These are my docs  oauth 1.0 and search.foods.v2
https://platform.fatsecret.com/api/Default.aspx?screen=rapiauth1

https://platform.fatsecret.com/api/Default.aspx?screen=rapiref&method=foods.search.v2

and I did everything I can (encoded every parameter values, implemented exactly same with php library   
 https://platform.fatsecret.com/api/Default.aspx?screen=lib) with my code for 3 days.. 

For those who succeeded in food.search.v2 POST with dart, I would appreciate it if you could check my code

Postman works fine and here is my code

Future<void> searchFoods() async {
var baseUrl = 'https://platform.fatsecret.com/rest/server.api';
var method = 'foods.search.v2';
var oauthConsumerKey = 'xxxxx';
var consumerSecret = 'xxxxx';
var tokenSecret = '';
var oauthNonce = generateNonce();
var oauthSignatureMethod = 'HMAC-SHA1';
var oauthTimestamp =
DateTime.now().millisecondsSinceEpoch.toString();
var oauthVersion = '1.0';
var format = 'json';
var searchExpression = '메밀면';
var maxResults = 10;
var includeSubCategories = true;
var flagDefaultServing = true;
var region = 'KR';
var language = 'en';

var params = {
'flag_default_serving': flagDefaultServing.toString(),
'format': format,
'include_sub_categories': includeSubCategories.toString(),
'language': language,
'max_results': maxResults.toString(),
'method': method,
'oauth_consumer_key': oauthConsumerKey,
'oauth_nonce': oauthNonce,
'oauth_signature_method': oauthSignatureMethod,
'oauth_timestamp': oauthTimestamp,
'oauth_version': oauthVersion,
'region': region,
'search_expression': searchExpression,
};

// Sort parameters by key
var sortedParams = params.keys.toList()
..sort((a, b) => (a.compareTo(b) ?? 0));

// Construct the parameter string
var paramString = sortedParams.map((key) {
var value = params[key];
return '${Uri.encodeComponent(key)}=${Uri.encodeComponent(value!)}';
}).join('&');

print(paramString);

var baseString =
'POST&${Uri.encodeComponent(baseUrl)}&${Uri.encodeComponent(paramString)}';

print(baseString);

var signingKey =
'${Uri.encodeComponent(consumerSecret)}&${Uri.encodeComponent(tokenSecret)}';
var hmacSha1 = Hmac(sha1, utf8.encode(signingKey));
var signatureBytes = hmacSha1.convert(utf8.encode(baseString)).bytes;
var signature = base64.encode(signatureBytes);

print(signature);
var oauthSignature = Uri.encodeComponent(signature);
print(oauthSignature);

var queryParams = {
'method': method,
'search_expression': searchExpression,
'format': format,
'include_sub_categories': includeSubCategories.toString(),
'flag_default_serving': flagDefaultServing.toString(),
'max_results': maxResults.toString(),
'language': language,
'region': region,
'oauth_consumer_key': oauthConsumerKey,
'oauth_nonce': oauthNonce,
'oauth_signature': oauthSignature,
'oauth_signature_method': oauthSignatureMethod,
'oauth_timestamp': oauthTimestamp,
'oauth_version': oauthVersion,
};

// var base = baseUrl.split('?');
// print(base[0]);

final response = await http.post(Uri.parse(baseUrl), body: queryParams);

print('요청간다');
if (response.statusCode == 200) {
// 응답 처리
print(response.body);
} else {
// 요청 실패 처리
print('API request failed');
}
}

String generateNonce() {
const chars =
'0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
final random = Random();
final nonceLength = 11;

final nonce =
List.generate(nonceLength, (_) => chars[random.nextInt(chars.length)])
.join();
return nonce;
}
}


Thank you all :)
and Thank you Sebastian for your continued support, but I'm sorry and upset that the problem has not been solved 

스크린샷 2023-05-27 03.58.56.png

Armando Soto

unread,
May 26, 2023, 11:48:59 PM5/26/23
to fatsecret-p...@googlegroups.com
Hi there,

Hope it is not too late, here is the code i used when i was doing also that.

import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:cancellation_token_http/http.dart' as http;
import 'dart:convert';
import 'package:crypto/crypto.dart';
import 'package:nonce/nonce.dart';

// TwitterApi class adapted from DanTup:
// https://blog.dantup.com/2017/01/simplest-dart-code-to-post-a-tweet-using-oauth/
class FatSecretApi {
  final String fatSecretApiBaseUrl = "platform.fatsecret.com";

  bool isJson = true;

  final String consumerKey, consumerKeySecret, accessToken, accessTokenSecret;

  late Hmac _sigHasher;

  FatSecretApi(this.consumerKey, this.consumerKeySecret, this.accessToken,
      this.accessTokenSecret) {
    var bytes = utf8.encode("$consumerKeySecret&$accessTokenSecret");
    _sigHasher = Hmac(sha1, bytes);
  }

  FatSecretApi forceXml() {
    this.isJson = false;
    return this;
  }

  /// Sends a tweet with the supplied text and returns the response from the Twitter API.
  Future<http.Response> request(
      Map<String, String> data, http.CancellationToken? canceltoken) {
    if (isJson) {
      data["format"] = "json";
    }
    return _callGetApi("rest/server.api", data, canceltoken);
  }

  Future<http.Response> _callGetApi(String url, Map<String, String> data,
      http.CancellationToken? canceltoken) {
    Uri requestUrl = Uri.https(fatSecretApiBaseUrl, url);

    _setAuthParams("GET", requestUrl.toString(), data);

    requestUrl = Uri.https(requestUrl.authority, requestUrl.path, data);

    String oAuthHeader = _generateOAuthHeader(data);
    if (canceltoken != null) {
      return _sendGetRequest(requestUrl, oAuthHeader, canceltoken);
    }
    // Build the OAuth HTTP Header from the data.
    // Build the form data (exclude OAuth stuff that's already in the header).
//    var formData = _filterMap(data, (k) => !k.startsWith("oauth_"));
    return _sendGetRequest(requestUrl, oAuthHeader, null);
  }

  void _setAuthParams(
      String requestMethod, String url, Map<String, String> data) {
    // Timestamps are in seconds since 1/1/1970.
    // var timestamp = new DateTime.now().toUtc().difference(_epochUtc).inSeconds;
    var millisecondsSinceEpoch = DateTime.now().toUtc().millisecondsSinceEpoch;
    var timestamp = (millisecondsSinceEpoch / 100).round();

    // Add all the OAuth headers we'll need to use when constructing the hash.
    data["oauth_consumer_key"] = consumerKey;
    data["oauth_signature_method"] = "HMAC-SHA1";
    data["oauth_timestamp"] = timestamp.toString();
    data["oauth_nonce"] = Nonce.unique(24).toString();
    // Required, but Twitter doesn't appear to use it
    if (accessToken != null && accessToken.isNotEmpty)
      data["oauth_token"] = accessToken;
    data["oauth_version"] = "1.0";

    // Generate the OAuth signature and add it to our payload.
    data["oauth_signature"] =
        _generateSignature(requestMethod, Uri.parse(url), data);
  }

  /// Generate an OAuth signature from OAuth header values.
  String _generateSignature(
      String requestMethod, Uri url, Map<String, String> data) {
    var sigString = _toQueryString(data);
    var fullSigData =
        "$requestMethod&${_encode(url.toString())}&${_encode(sigString)}";

    return base64.encode(_hash(fullSigData));
  }

  /// Generate the raw OAuth HTML header from the values (including signature).
  String _generateOAuthHeader(Map<String, String> data) {
    var oauthHeaderValues = _filterMap(data, (k) => k.startsWith("oauth_"));

    return "OAuth " + _toOAuthHeader(oauthHeaderValues);
  }

  /// Send HTTP Request and return the response.
  Future<http.Response> _sendGetRequest(Uri fullUrl, String oAuthHeader,
      http.CancellationToken? canceltoken) async {
    if (canceltoken != null) {
      await http.get(fullUrl, headers: {}, cancellationToken: canceltoken);
    }
    return await http.get(fullUrl, headers: {});
  }

  Map<String, String> _filterMap(
      Map<String, String> map, bool test(String key)) {
    return new Map.fromIterable(map.keys.where(test),
        value: (k) => map[k] ?? "");
  }

  String _toQueryString(Map<String, String> data) {
    var items = data.keys.map((k) => "$k=${_encode(data[k] ?? "")}").toList();
    items.sort();

    return items.join("&");
  }

  String _toOAuthHeader(Map<String, String> data) {
    var items =
        data.keys.map((k) => "$k=\"${_encode(data[k] ?? "")}\"").toList();
    items.sort();

    return items.join(", ");
  }

  List<int> _hash(String data) => _sigHasher.convert(data.codeUnits).bytes;

  String _encode(String data) => Uri.encodeComponent(data);

  String _randomString(int length) {
    var rand = new Random();
    var codeUnits = new List.generate(length, (index) {
      return rand.nextInt(26) + 97;
    });

    return String.fromCharCodes(codeUnits);
  }
}


Happy Coding!

Armando

--
You received this message because you are subscribed to the Google Groups "FatSecret Platform API" group.
To unsubscribe from this group and stop receiving emails from it, send an email to fatsecret-platfor...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/fatsecret-platform-api/94471836-e466-4c42-b25b-e01e05893ee0n%40googlegroups.com.

김도연

unread,
May 28, 2023, 9:27:32 PM5/28/23
to fatsecret-p...@googlegroups.com
Hi Armando!
Thanks for your help :)

It's strange to send a get different from the homepage document.. And baseurl is different

Can I see an example of actually sending a request using this class? For example, I was checking if I got a response when I searched 'corn'.

You can cover sensitive information!
Thank you.
Doyeon


2023년 5월 27일 (토) 오후 12:49, Armando Soto <dev.ar...@gmail.com>님이 작성:

김도연

unread,
May 28, 2023, 9:27:38 PM5/28/23
to fatsecret-p...@googlegroups.com
Oh i succeed!!!!
Thanks a lot !!!!!! 

2023년 5월 28일 (일) 오후 7:59, 김도연 <syd...@lansik.us>님이 작성:

Armando Soto

unread,
May 28, 2023, 11:23:42 PM5/28/23
to fatsecret-p...@googlegroups.com
Hi there,

I stopped checking on it since y move on with creating fastsecret integration on rails my bad. hahaha, your welcome.

Happy Coding!

Armando

Reply all
Reply to author
Forward
0 new messages