Вот код, который я использовал для подключения к ETrade API (проверено и работает).
Одно предостережение: вам необходимо реализовать собственное хранилище пользовательских токенов. Я не включил это здесь, поскольку код, который я создал, сильно зависит от домена.
Сначала я добавил DotNetOpenAuth
в проект и создал ETradeConsumer
(он получен из WebConsumer DotNetOpenAuth):
EtradeConsumer.cs
public static class ETradeConsumer
{
public static string AccessUrl
{
get
{
return "https://etws.etrade.com/oauth/access_token";
}
}
public static string RequestUrl
{
get
{
return "https://etws.etrade.com/oauth/request_token";
}
}
public static string UserAuthorizedUrl
{
get
{
return "https://us.etrade.com/e/t/etws/authorize";
}
}
private static readonly ServiceProviderDescription ServiceProviderDescription = new ServiceProviderDescription()
{
AccessTokenEndpoint = new MessageReceivingEndpoint(AccessUrl, HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
ProtocolVersion = ProtocolVersion.V10a,
RequestTokenEndpoint = new MessageReceivingEndpoint(RequestUrl, HttpDeliveryMethods.PostRequest | HttpDeliveryMethods.AuthorizationHeaderRequest),
TamperProtectionElements = new ITamperProtectionChannelBindingElement[] { new HmacSha1SigningBindingElement() },
UserAuthorizationEndpoint = new MessageReceivingEndpoint(new Uri(UserAuthorizedUrl), HttpDeliveryMethods.GetRequest | HttpDeliveryMethods.AuthorizationHeaderRequest)
};
public static DesktopConsumer CreateConsumer(IConsumerTokenManager tokenManager)
{
return new DesktopConsumer(ServiceProviderDescription, tokenManager);
}
public static Uri PrepareRequestAuthorization(DesktopConsumer consumer, out string requestToken)
{
if (consumer == null)
{
throw new ArgumentNullException("consumer");
}
Uri authorizationUrl = consumer.RequestUserAuthorization(null, null, out requestToken);
authorizationUrl = new Uri(string.Format("{0}?key={1}&token={2}", ServiceProviderDescription.UserAuthorizationEndpoint.Location.AbsoluteUri, consumer.TokenManager.ConsumerKey, requestToken));
return authorizationUrl;
}
public static AuthorizedTokenResponse CompleteAuthorization(DesktopConsumer consumer, string requestToken, string userCode)
{
var customServiceDescription = new ServiceProviderDescription
{
RequestTokenEndpoint = ServiceProviderDescription.RequestTokenEndpoint,
UserAuthorizationEndpoint =
new MessageReceivingEndpoint(
string.Format("{0}?key={1}&token={2}", ServiceProviderDescription.UserAuthorizationEndpoint.Location.AbsoluteUri,
consumer.TokenManager.ConsumerKey, requestToken),
HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
AccessTokenEndpoint = new MessageReceivingEndpoint(
ServiceProviderDescription.AccessTokenEndpoint.Location.AbsoluteUri + "?oauth_verifier" + userCode + string.Empty,
HttpDeliveryMethods.AuthorizationHeaderRequest | HttpDeliveryMethods.GetRequest),
TamperProtectionElements = ServiceProviderDescription.TamperProtectionElements,
ProtocolVersion = ProtocolVersion.V10a
};
var customConsumer = new DesktopConsumer(customServiceDescription, consumer.TokenManager);
var response = customConsumer.ProcessUserAuthorization(requestToken, userCode);
return response;
}
}
Во-вторых, вам нужно создать класс для управления токенами Etrade. В качестве примера я создал следующий класс. Он управляет токенами через InMemoryCollection, но на самом деле его следует хранить где-то в другом месте (в базе данных, в файле cookie или в другом месте, чтобы пользователю не приходилось каждый раз проходить аутентификацию / авторизацию). Токены ConsumerKey
и ConsumerSecret
- это то, на что вы подписываетесь через Etrade:
public class ETradeTokenManager : IConsumerTokenManager
{
private Dictionary<string, string> tokensAndSecrets = new Dictionary<string, string>();
public string ConsumerKey { get { return "YourConsumerKey"; } }
public string ConsumerSecret { get { return "YourConsumerSecret"; } }
public string GetTokenSecret(string token)
{
return tokensAndSecrets[token];
}
public void StoreNewRequestToken(UnauthorizedTokenRequest request, ITokenSecretContainingMessage response)
{
tokensAndSecrets[response.Token] = response.TokenSecret;
}
public void ExpireRequestTokenAndStoreNewAccessToken(string consumerKey, string requestToken, string accessToken, string accessTokenSecret)
{
tokensAndSecrets.Remove(requestToken);
tokensAndSecrets[accessToken] = accessTokenSecret;
}
public TokenType GetTokenType(string token)
{
throw new NotImplementedException();
}
}
Наконец, добавьте следующее (я использовал ASP.NET MVC 3. Ваша структура может отличаться):
public ActionResult EtradeAuthorize(string returnUrl)
{
var consumer = ETradeConsumer.CreateConsumer(TokenManager);
string requestToken;
Uri popupWindow = ETradeConsumer.PrepareRequestAuthorization(consumer, out requestToken);
var etradeViewModel = new ETradeAuthorizeViewModel(popupWindow, requestToken);
return View(etradeViewModel);
}
[HttpPost]
public ActionResult CompleteAuthorization(FormCollection formCollection)
{
string accessToken = "";
var consumer = ETradeConsumer.CreateConsumer(TokenManager);
var authorizationReponse = ETradeConsumer.CompleteAuthorization(consumer, formCollection["requestToken"], formCollection["userCode"]);
if (authorizationReponse != null)
{
accessToken = authorizationReponse.AccessToken;
}
var etradeViewModel = new ETradeCompleteAuthorizeViewModel(formCollection["requestToken"], formCollection["userCode"], accessToken);
return View(etradeViewModel);
}
Если вы получите 400 Bad Request
, возьмите callbackUrl
для Etrade. По какой-то причине он генерирует неверный запрос всякий раз, когда используется URL-адрес обратного вызова. Они предпочитают oob
(вне группы). Чтобы использовать oob
, установите null
на URL обратного вызова в методе Consumer.Channel.Send()
.
Есть и другие проблемы. Эта проблема: Due to a logon delay or other issue, your authentication could not be completed at this time. Please try again.
вызвана тем, что authorize
часть вызова не обрабатывается должным образом. В частности, Etrade требует, чтобы URL авторизации выглядел следующим образом:
https://us.etrade.com/e/t/etws/authorize?key={yourConsumerKey}&token={requestToken}
Спецификация OAuth требует, чтобы токен запроса был request_token={requestToken}
, а не token={requestToken}
.
Мне не удалось заставить API Etrade правильно авторизоваться с помощью WebConsumer
, но как только я переключился на Desktop Consumer
и самостоятельно обработал запрос, он работал правильно.