如何使用OAuth连接到Etrade API?

E-Trade最近发布了他们的API,并提供了一些有用但不完整的技术文档 。

有没有人在C#中有一个完整的工作示例,说明这是如何工作的?

我已经能够正确地使用OAuth进行身份validation,但是当从我的帐户或市场数据中获取信息时,服务器会失败。

我能够使用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(); } } 

这是我用来连接到ETrade API的代码(经过测试和工作)。

一个警告:您需要实现自己的用户令牌存储。 我没有在此处包含,因为我创建的代码是高度特定于域的。

首先,我将DotNetOpenAuth添加到项目中并创建了一个ETradeConsumer (它来自DotNetOpenAuth的WebConsumer):

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,或者某些东西,以便用户不必每次都进行身份validation/授权)。 ConsumerKeyConsumerSecret令牌是您通过Etrade注册的内容:

 public class ETradeTokenManager : IConsumerTokenManager { private Dictionary tokensAndSecrets = new Dictionary(); 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 ,请取出Etrade的callbackUrl 。 出于某种原因,只要使用回调URL,它就会抛出错误的请求。 他们更喜欢oob (Out of Band)。 要使用oob ,请将null设置为Consumer.Channel.Send()方法中的回调URL。

还有其他问题。 此问题: Due to a logon delay or other issue, your authentication could not be completed at this time. Please try again. 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}

我无法通过WebConsumer正确授权Etrade API,但是一旦我切换到Desktop Consumer并自己操作请求,它就能正常工作。

如果您收到“由于登录延迟或其他问题,您的身份validation目前无法完成。请重试。”,

那么我认为你在授权url中输入了错误的密钥。

您应该通过替换此格式的相应密钥来关注该文档

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

要使用jejernig的答案中的示例类+ GitHub的代码,我使用了以下内容:

 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); 

我刚从BaseOAuthRepository删除了abstract ,并且必须修复GitHub代码,因为IOAuthSession.GetUserAuthorizationUrlForToken()的参数被反转(我改变了其余代码以匹配接口的参数)。

我感到害怕Due to a logon delay or other issue, your authentication could not be completed at this time. Please try again. Due to a logon delay or other issue, your authentication could not be completed at this time. Please try again. 消息,但这可能是由于我必须解决的实际登录问题。

清除Cookie并重试。 我不确定,为什么会这样。 但是,一旦出现此错误,除非您清除cookie,否则您将收到相同的错误。 我能够成功登录并调用一些REST服务。