Как использовать OAuth для подключения к Etrade API? - PullRequest
22 голосов
/ 26 сентября 2010

E-Trade недавно выпустила свой API и предоставила техническую документацию, которая является несколько полезной, но не полной .

Есть ли у кого-нибудь полностью рабочий пример в C #, который показывает, как это работает?

Мне удалось правильно выполнить аутентификацию с использованием OAuth, но когда дело доходит до получения информации из моего аккаунтаили рыночные данные, серверы выходят из строя.

Ответы [ 5 ]

8 голосов
/ 29 июля 2011

Мне удалось подключиться с помощью библиотеки DevDefined OAuth, но мне пришлось сделать несколько твиков источника, чтобы он работал правильно.Я разветвил репозиторий, чтобы вы могли скачать src, который я использовал, и собрать вам .dll.

Репо: GitHub

Пример класса:

 public abstract class BaseOAuthRepository
{

    private static string REQUEST_URL = "https://etws.etrade.com/oauth/request_token";
    private static string AUTHORIZE_URL = "https://us.etrade.com/e/t/etws/authorize";
    private static string ACCESS_URL = "https://etws.etrade.com/oauth/access_token";

    private readonly TokenBase _tokenBase;
    private readonly string _consumerSecret;

    protected BaseOAuthRepository(TokenBase tokenBase, 
                                  string consumerSecret)
    {
        _tokenBase = tokenBase;
        _consumerSecret = consumerSecret;
    }

    public TokenBase MyTokenBase
    {
        get { return _tokenBase; }
    }

    public string MyConsumerSecret
    {
        get { return _consumerSecret; }
    }


    public OAuthSession CreateSession()
    {
        var consumerContext = new OAuthConsumerContext
        {
            ConsumerKey = MyTokenBase.ConsumerKey,
            ConsumerSecret = MyConsumerSecret,
            SignatureMethod = SignatureMethod.HmacSha1,
            UseHeaderForOAuthParameters = true,
            CallBack = "oob"
        };

        var session = new OAuthSession(consumerContext, REQUEST_URL, AUTHORIZE_URL, ACCESS_URL);    
        return session;
    }

    public IToken GetAccessToken(OAuthSession session)
    {
        IToken requestToken = session.GetRequestToken();
        string authorizationLink = session.GetUserAuthorizationUrlForToken(MyTokenBase.ConsumerKey, requestToken);
        Process.Start(authorizationLink);
        Console.Write("Please enter pin from browser: ");
        string pin = Console.ReadLine();
        IToken accessToken = session.ExchangeRequestTokenForAccessToken(requestToken, pin.ToUpper());

        return accessToken;
    }

    public string GetResponse(OAuthSession session, string url)
    {
        IToken accessToken = MyTokenBase;

        var response = session.Request(accessToken).Get().ForUrl(url).ToString();
        return response;
    }

    public XDocument GetWebResponseAsXml(HttpWebResponse response)
    {
        XmlReader xmlReader = XmlReader.Create(response.GetResponseStream());
        XDocument xdoc = XDocument.Load(xmlReader);
        xmlReader.Close();
        return xdoc;
    }

    public string GetWebResponseAsString(HttpWebResponse response)
    {
        Encoding enc = System.Text.Encoding.GetEncoding(1252);
        StreamReader loResponseStream = new
        StreamReader(response.GetResponseStream(), enc);
        return loResponseStream.ReadToEnd();
    }
}
7 голосов
/ 26 октября 2011

Вот код, который я использовал для подключения к 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 и самостоятельно обработал запрос, он работал правильно.

1 голос
/ 15 апреля 2012

Чтобы использовать пример класса + код GitHub из ответа Jejernig , я использовал следующее:

TokenBase token = new TokenBase { ConsumerKey = "oauth_consumer_key from ETRADE" }; // OAuthRepository only seems to use the consumer key
OAuthRepository rep = new OAuthRepository(token, "consumer_secret from ETRADE");
OAuthSession session = rep.CreateSession();
IToken accessToken = rep.GetAccessToken(session);

Я только что удалил abstract из BaseOAuthRepository, и мне пришлось исправить код GitHub, потому что параметры IOAuthSession.GetUserAuthorizationUrlForToken() были перевернуты (я изменил остальную часть кода, чтобы соответствовать параметрам интерфейса).

Я получаю страшное сообщение Due to a logon delay or other issue, your authentication could not be completed at this time. Please try again., но это может быть связано с реальной проблемой входа, которую мне нужно решить.

1 голос
/ 04 августа 2011

Если вы получаете "Из-за задержки входа в систему или другой проблемы ваша аутентификация не может быть завершена в настоящее время. Пожалуйста, попробуйте еще раз.",

тогда я думаю, что вы вставили неправильные ключи в URL авторизации.

Вы должны следовать документу, заменяя соответствующие ключи в этом формате

https://us.etrade.com/e/etws/authorize?key=&token=

0 голосов
/ 27 февраля 2011

Очистите куки и попробуйте снова.Я не уверен, почему это происходит.Но, как только вы получите эту ошибку, если только вы не удалите куки, вы получите ту же ошибку.Я могу успешно войти в систему и позвонить в некоторые из служб REST.

...