{"error":"id: 3931, ошибка: недопустимый потребитель","statusCode":401
- Конечная точка, которую я пытаюсь заставить работать : https://www.interactivebrokers.com/webt ... token/post
- Спецификация OAuth v1.0a https://oauth .net/core/1.0a/#auth_header_authorization
Это тоже на GitHub.
var httpClient = new HttpClient
{
BaseAddress = new Uri("https://www.interactivebrokers.com/tradingapi/v1/")
};
var restClient = new IBRestClient(httpClient);
var response = await restClient.RequestTokenAsync("xxxxx");
Console.WriteLine($"Response: {response}");
Console.ReadLine();
public sealed class IBRestClient
{
private readonly HttpClient _httpClient;
public IBRestClient(HttpClient httpClient)
{
_httpClient = httpClient;
}
public async ValueTask RequestTokenAsync(string consumerKey)
{
const string requestUri = "oauth/request_token";
var request = new HttpRequestMessage(HttpMethod.Post, requestUri);
var baseUrl = _httpClient.BaseAddress!.AbsoluteUri;
var authorizationHeader = OAuthHelper.GetAuthorizationHeader($"{baseUrl}{requestUri}", "POST", consumerKey);
var authSplit = authorizationHeader.Split(' ');
request.Headers.Authorization = new AuthenticationHeaderValue(authSplit[0], authSplit[1]);
var response = await _httpClient.SendAsync(request);
return await response.Content.ReadAsStringAsync();
}
}
public static class OAuthHelper
{
private static readonly RandomNumberGenerator Random = RandomNumberGenerator.Create();
public static string GetAuthorizationHeader(string uri, string method, string consumerKey)
{
var oauthParameters = new Dictionary
{
{ "oauth_consumer_key", consumerKey },
{ "oauth_signature_method", "RSA-SHA256" },
{ "oauth_timestamp", GetTimestamp() },
{ "oauth_nonce", GetNonce() },
{ "oauth_callback", "oob" }
};
// The request parameters are collected, sorted and concatenated into a normalized string
var queryParameters = ExtractQueryParams(uri);
var oauthParamString = GetOAuthParamString(queryParameters, oauthParameters);
var baseUri = GetBaseUriString(uri);
// Signature Base String
var signatureBaseString = GetSignatureBaseString(baseUri, method, oauthParamString);
var pem = File.ReadAllText("private_encryption.pem");
var signingKey = RSA.Create();
signingKey.ImportFromPem(pem);
var signature = SignSignatureBaseString(signatureBaseString, Encoding.UTF8, signingKey);
oauthParameters.Add("oauth_signature", signature);
// Constructs and returns the Authorization header
var sb = new StringBuilder();
foreach (var param in oauthParameters)
{
sb
.Append(sb.Length == 0 ? "OAuth " : ",")
.Append(param.Key)
.Append("=\"")
.Append(ToUriRfc3986(param.Value))
.Append('"');
}
return sb.ToString();
}
///
/// Parse query parameters out of the URL.
///
private static Dictionary ExtractQueryParams(string uri)
{
var queryParamCollection = new Dictionary();
var beginIndex = uri.IndexOf('?');
if (beginIndex 0 ? "&" : string.Empty)
.Append(parameter.Key)
.Append('=')
.Append(value);
}
}
return parameterString.ToString();
}
///
/// Normalizes the URL.
///
private static string GetBaseUriString(string uriString)
{
var uri = new Uri(uriString);
var lowerCaseScheme = uri.Scheme.ToLower();
var lowerCaseAuthority = uri.Authority.ToLower();
var path = uri.AbsolutePath;
if (("http".Equals(lowerCaseScheme) && uri.Port == 80) || ("https".Equals(lowerCaseScheme) && uri.Port == 443))
{
// Remove port if it matches the default for scheme
var index = lowerCaseAuthority.LastIndexOf(':');
if (index >= 0)
{
lowerCaseAuthority = lowerCaseAuthority[..index];
}
}
if (string.IsNullOrEmpty(path))
{
path = "/";
}
return $"{lowerCaseScheme}://{lowerCaseAuthority}{path}"; // Remove query and fragment
}
///
/// The Signature Base String is a consistent reproducible concatenation of the request elements into a single string.
///
private static string GetSignatureBaseString(string baseUri, string httpMethod, string oauthParamString)
{
return httpMethod.ToUpper() // Uppercase HTTP method
+ "&" + ToUriRfc3986(baseUri) // Base URI
+ "&" + ToUriRfc3986(oauthParamString); // OAuth parameter string
}
///
/// Signs the signature base string using an RSA private key.
///
private static string SignSignatureBaseString(string baseString, Encoding encoding, RSA privateKey)
{
var hash = Sha256Digest(baseString, encoding);
var signedHashValue = privateKey.SignHash(hash, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
return Convert.ToBase64String(signedHashValue);
}
///
/// Percent encodes entities.
///
private static string ToUriRfc3986(string input)
{
if (string.IsNullOrEmpty(input))
{
return input;
}
var escaped = new StringBuilder(Uri.EscapeDataString(input));
string[] uriRfc3986EscapedChars = { "!", "*", "'", "(", ")" };
foreach (var escapedChar in uriRfc3986EscapedChars)
{
escaped.Replace(escapedChar, UriHelper.HexEscape(escapedChar[0]));
}
return escaped.ToString();
}
///
/// Returns a cryptographic hash of the given input.
///
private static byte[] Sha256Digest(string input, Encoding encoding)
{
var inputBytes = encoding.GetBytes(input);
return SHA256.HashData(inputBytes);
}
///
/// Generates a 16 char random string for replay protection.
///
private static string GetNonce()
{
var data = new byte[8];
Random.GetBytes(data);
return BitConverter.ToString(data).Replace("-", string.Empty).ToLower();
}
///
/// Returns UNIX Timestamp.
///
private static string GetTimestamp()
{
return DateTimeOffset.UtcNow.ToUnixTimeSeconds().ToString();
}
}
internal static class UriHelper
{
private static readonly char[] HexUpperChars =
{
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
};
internal static string HexEscape(char character)
{
if (character > '\xff')
{
throw new ArgumentOutOfRangeException(nameof(character));
}
var chars = new char[3];
var pos = 0;
EscapeAsciiChar(character, chars, ref pos);
return new string(chars);
}
private static void EscapeAsciiChar(char ch, char[] to, ref int pos)
{
to[pos++] = '%';
to[pos++] = HexUpperChars[(ch & 0xf0) >> 4];
to[pos++] = HexUpperChars[ch & 0xf];
}
}
Подробнее здесь: https://stackoverflow.com/questions/750 ... -oauth-tok
Мобильная версия