I'm getting this error when i trying to get oauth token with Here APi
Here is the error
wrong.","error":"invalid_client","error_description":"errorCode: '401300'. Signature mismatch. Authorization signature or client credential is wrong."}
This is my oauth code in Flutter
import 'dart:async';
import 'dart:convert';
import 'dart:math';
import 'package:http/http.dart' as http;
import 'package:convert/convert.dart';
import 'package:crypto/crypto.dart';
import 'package:let_log/let_log.dart';
class Oauthv1 {
String oauthv1BaseUrl = "account.api.here.com";
bool isJson = false;
final String consumerKey, consumerKeySecret, accessToken, accessTokenSecret;
Hmac _sigHasher;
Oauthv1(this.consumerKey, this.consumerKeySecret, this.accessToken,
this.accessTokenSecret) {
var bytes = utf8.encode("$consumerKeySecret");
_sigHasher = new Hmac(sha256, bytes);
}
Oauthv1 forceXml() {
this.isJson = false;
return this;
}
Future<http.Response> request(Map<String, String> data) {
if (isJson) {
data["format"] = "json";
}
return _callGetApi("oauth2/token", data);
}
Future<http.Response> _callGetApi(String url, Map<String, String> data) {
Uri requestUrl = Uri.https(oauthv1BaseUrl, url);
print(data["grant_type"]);
_setAuthParams("POST", requestUrl.toString(), data);
requestUrl = Uri.https(requestUrl.authority, requestUrl.path, data);
String oAuthHeader = _generateOAuthHeader(data);
Map<String, String> _headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': '$oAuthHeader'
};
//Logger.debug(_headers);
// 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_"));
//Logger.debug(requestUrl);
//Logger.warn(data);
Logger.debug(_headers);
return _sendGetRequest(requestUrl, {'grant_type': 'client_credentials'}, _headers);
}
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 =
new DateTime.now().toUtc().millisecondsSinceEpoch;
var timestamp = (millisecondsSinceEpoch / 100).round(); */
var ms = (new DateTime.now()).millisecondsSinceEpoch;
var timestamp = (ms / 1000).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-SHA256";
data["oauth_timestamp"] = timestamp.toString();
data["oauth_nonce"] =
_randomString(8); // 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, Map<String, String> data,
Map<String, String> headers) async {
return await http.post("https://account.api.here.com/oauth2/token", body: 'grant_type=client_credentials', headers: 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) => percent.encode(data.codeUnits);
String _randomString(int length) {
var rand = new Random();
var codeUnits = new List.generate(length, (index) {
return rand.nextInt(26) + 97;
});
return new String.fromCharCodes(codeUnits);
}
}
I send all headers and body request but response failed. The body request is
grant_type=client_credentials
I don't really know which data when not send or which param is missed. I'm using flutter Oauth 1.0. The auth working fine in postman but integration in flutter not work.
When i print _headers from sendgetrequest() method, it print this : {Content-Type: application/x-www-form-urlencoded, Authorization: OAuth oauth_consumer_key="o2zr*****bXuA", oauth_nonce="oaqpiovg", oauth_signature="H7M92BoEneotellYHqJCMkMfLOq9sMrm1R5KdtS8lAM%3D", oauth_signature_method="HMAC-SHA256", oauth_timestamp="1601165843", oauth_version="1.0"}
you can see the headers is correctly formated
I solved this by follow step by step: Create OAuth 1.0 signature section on here Documentation and it worked fine
The problem was that the order was not followed correctly in the oauth parameters.