E*Trade API 在获取访问令牌时经常返回 HTTP 401 Unauthorized,但并非总是如此

2023-12-02

Summary

我编写了一个简单的 C# .NET Core 应用程序,使用 OAuthv1 对 E*Trade API 进行身份验证,目的是获取股票报价。我能够进行身份验证并获取请求令牌,重定向到授权页面并获取验证程序字符串。但是,当我使用验证程序字符串执行访问令牌请求时,大约十分之九的情况是我收到 401 未经授权。但有时它会起作用,我会取回访问令牌。

Details

  • 我正在使用 .NET OAuth 类 OAuthRequest 来生成查询 字符串授权参数。
  • 我正在使用这个APIhttps://apisb.etrade.com/docs/api/authorization/get_access_token.html#
  • 我已经下载了这个示例应用程序并比较了 URL 使用过并且没有发现可以解释这一点的重大差异 行为。https://cdn2.etrade.net/1/18122609420.0/aempros/content/dam/etrade/developer-site/en_US/document/downloads/EtradePythonClient.zip
  • 示例应用程序每次都可以使用我的信用信息,因此我知道它们是有效的。 C# 代码生成导致此问题的签名(可能)的方式存在一些差异,并且它显然是不确定的,因为有时我的应用程序可以工作。
  • 我比较了示例应用程序和我的应用程序之间用于身份验证的 URL,它们是相同的。

Code

为了理智起见,我创建了单独的请求对象,我不会这样。同样,我能够获取请求令牌、重定向以授权并获取验证程序字符串,但不能获取访问令牌。

    private static async Task FetchData()
    {  
        // Values
        string consumerKey = "...";
        string consumerSecret = "...";
        string requestTokenUrl = "https://api.etrade.com/oauth/request_token";
        string authorizeUrl = "https://us.etrade.com/e/t/etws/authorize";
        string accessTokenUrl = "https://api.etrade.com/oauth/access_token";
        string quoteUrl = "https://api.etrade.com/v1/market/quote/NVDA,DJI";

        // Create the request 
        var request = new OAuthRequest
        {
            Type = OAuthRequestType.RequestToken,
            ConsumerKey = consumerKey,
            ConsumerSecret = consumerSecret,
            Method = "GET",
            RequestUrl = requestTokenUrl,
            Version = "1.0",
            Realm = "etrade.com",
            CallbackUrl = "oob",
            SignatureMethod = OAuthSignatureMethod.HmacSha1
        };

        // Make call to fetch session token
        try
        {
            HttpClient client = new HttpClient();
            
            var requestTokenUrlWithQuery = $"{requestTokenUrl}?{request.GetAuthorizationQuery()}";
            var responseString = await client.GetStringAsync(requestTokenUrlWithQuery);
            var tokenParser = new TokenParser(responseString, consumerKey);

            // Call authorization API
            var authorizeUrlWithQuery = $"{authorizeUrl}?{tokenParser.GetQueryString()}";
            
            // Open browser with the above URL 
            ProcessStartInfo psi = new ProcessStartInfo
            {
                FileName = authorizeUrlWithQuery,
                UseShellExecute = true
            };
            Process.Start(psi);

            // Request input of token, copied from browser
            Console.Write("Provide auth code:");
            var authCode = Console.ReadLine();
           
            // Need auth token and verifier
            var secondRequest = new OAuthRequest
            {
                Type = OAuthRequestType.AccessToken,
                ConsumerKey = consumerKey,
                ConsumerSecret = consumerSecret,
                SignatureMethod = OAuthSignatureMethod.HmacSha1,
                Method = "GET",
                Token = tokenParser.Token,
                TokenSecret = tokenParser.Secret,
                Verifier = authCode,
                RequestUrl = accessTokenUrl,
                Version = "1.0",
                Realm = "etrade.com"
            };

            // Make access token call
            var accessTokenUrlWithQuery = $"{accessTokenUrl}?{secondRequest.GetAuthorizationQuery()}";
            responseString = await client.GetStringAsync(accessTokenUrlWithQuery);

            Console.WriteLine("Access token: " + responseString);

            // Fetch quotes
            tokenParser = new TokenParser(responseString, consumerKey);
            var thirdRequest = new OAuthRequest
            {
                Type = OAuthRequestType.ProtectedResource,
                ConsumerKey = consumerKey,
                ConsumerSecret = consumerSecret,
                SignatureMethod = OAuthSignatureMethod.HmacSha1,
                Method = "GET",
                Token = tokenParser.Token,
                TokenSecret = tokenParser.Secret,
                RequestUrl = quoteUrl,
                Version = "1.0",
                Realm = "etrade.com"
            };
            
            var quoteUrlWithQueryString = $"{quoteUrl}?{thirdRequest.GetAuthorizationQuery()}";
            responseString = await client.GetStringAsync(quoteUrlWithQueryString);

            // Dump data to console 
            Console.WriteLine(responseString);
            
        }
        catch (Exception ex)
        {
            Console.WriteLine("\n"+ ex.Message);
        }
    }

    class TokenParser {
        private readonly string consumerKey;

        public TokenParser(string responseString, string consumerKey)
        {
            NameValueCollection queryStringValues = HttpUtility.ParseQueryString(responseString);
            Token = HttpUtility.UrlDecode(queryStringValues.Get("oauth_token"));
            Secret = HttpUtility.UrlDecode(queryStringValues.Get("oauth_token_secret"));
            this.consumerKey = consumerKey;
        }

        public string Token { get; set; }
        public string Secret { get; private set; }

        public string GetQueryString()
        {
            return $"key={consumerKey}&token={Token}";
        }
    }

举个例子,在写这篇文章时,我运行了该应用程序几次,它运行了一次,失败了一次。我根本没有改变代码。


作为健全性检查,我将我的身份验证参数插入一个生成签名的网站,只是为了看看它是否与我从 OAuthRequest 中获得的相同。不是。我决定尝试一些不同的东西。我使用 RestSharp 实现了我的逻辑,并且几乎立即开始工作。这是代码。

// Values
        string consumerKey = "...";
        string consumerSecret = "...";
        string baseEtradeApiUrl = "https://api.etrade.com";
        string baseSandboxEtradeApiUrl = "https://apisb.etrade.com";
        string authorizeUrl = "https://us.etrade.com";  
        
        try
        {
            // Step 1: fetch the request token
            var client = new RestClient(baseEtradeApiUrl);
            client.Authenticator = OAuth1Authenticator.ForRequestToken(consumerKey, consumerSecret, "oob");
            IRestRequest request = new RestRequest("oauth/request_token");
            var response = client.Execute(request);
            Console.WriteLine("Request tokens: " + response.Content);

            // Step 1.a: parse response 
            var qs = HttpUtility.ParseQueryString(response.Content);
            var oauthRequestToken = qs["oauth_token"];
            var oauthRequestTokenSecret = qs["oauth_token_secret"];

            // Step 2: direct to authorization page
            var authorizeClient = new RestClient(authorizeUrl);
            var authorizeRequest = new RestRequest("e/t/etws/authorize");
            authorizeRequest.AddParameter("key", consumerKey);
            authorizeRequest.AddParameter("token", oauthRequestToken);
            ProcessStartInfo psi = new ProcessStartInfo
            {
                FileName = authorizeClient.BuildUri(authorizeRequest).ToString(),
                UseShellExecute = true
            };
            Process.Start(psi);

            Console.Write("Provide auth code:");
            var verifier = Console.ReadLine();

            // Step 3: fetch access token
            var accessTokenRequest = new RestRequest("oauth/access_token");
            client.Authenticator = OAuth1Authenticator.ForAccessToken(consumerKey, consumerSecret, oauthRequestToken, oauthRequestTokenSecret, verifier);
            response = client.Execute(accessTokenRequest);
            Console.WriteLine("Access tokens: " + response.Content);

            // Step 3.a: parse response 
            qs = HttpUtility.ParseQueryString(response.Content);
            var oauthAccessToken = qs["oauth_token"];
            var oauthAccessTokenSecret = qs["oauth_token_secret"];

            // Step 4: fetch quote
            var sandboxClient = new RestClient(baseSandboxEtradeApiUrl);
            var quoteRequest = new RestRequest("v1/market/quote/GOOG.json");
            sandboxClient.Authenticator = OAuth1Authenticator.ForProtectedResource(consumerKey, consumerSecret, oauthAccessToken, oauthAccessTokenSecret);
            response = sandboxClient.Execute(quoteRequest);
            Console.WriteLine("Quotes: " + response.Content);

        } catch(Exception ex)
        {
            Console.WriteLine(ex.Message);
        }

上面的逻辑是有效的。我对上一问题的唯一可行理论是签名定期无效。老实说,我不知道根本原因,但这个解决方案有效,所以我对此很满意。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

E*Trade API 在获取访问令牌时经常返回 HTTP 401 Unauthorized,但并非总是如此 的相关文章

  • 是否需要销毁运算符删除的形式才能真正销毁对象?

    C 20 添加了破坏形式operator delete区别于std destroying delete t范围 它导致delete表达式在调用之前不再销毁对象operator delete 目的是在显式调用对象的析构函数和释放内存之前 允许
  • 使用 ADAL v3 使用 ClientID 对 Dynamics 365 进行身份验证

    我正在尝试对我们的在线 Dynamics CRM 进行身份验证以使用可用的 API 我能找到的唯一关于执行此操作的官方文档是 https learn microsoft com en us dynamics365 customer enga
  • C#.Net 邮件将进入垃圾邮件文件夹

    我正在从 ASP net Web 应用程序发送电子邮件 邮件发送成功 没有失败 但大多数都进入了垃圾邮件文件夹 请帮助我克服垃圾邮件过滤器 我的发送邮件代码 public void SendMail string FromAddress s
  • 如何使用 openSSL 函数验证 PEM 证书的密钥长度

    如何验证以这种方式生成的 PEM 证书的密钥长度 openssl genrsa des3 out server key 1024 openssl req new key server key out server csr cp server
  • Boost ASIO 串行写入十六进制值

    我正在使用 ubuntu 通过串行端口与设备进行通信 所有消息都必须是十六进制值 我已经在 Windows 环境中使用白蚁测试了通信设置 并得到了我期望的响应 但在使用 Boost asio 时我无法得到任何响应 以下是我设置串口的方法 b
  • 如何在 C# 中将 Json 转换为对象

    我想将 Json 转换为 C 中的对象 这里的 Json 是 值 e920ce0f e3f5 4c6f 8e3d d2fbc51990e4 如何使用 Object 问题看似愚蠢 但其实并不那么愚蠢 我没有简单的 Json 我有 IEnume
  • C# 中一次性对象克隆会导致内存泄漏吗?

    检查这个代码 class someclass IDisposable private Bitmap imageObject public void ImageCrop int X int Y int W int H imageObject
  • 防止控制台应用程序中的内存工作集最小化?

    我想防止控制台应用程序中的内存工作集最小化 在Windows应用程序中 我可以这样做覆盖 SC MINIMIZE 消息 http support microsoft com kb 293215 en us fr 1 但是 如何在控制台应用程
  • 为什么这个 makefile 在“make clean”上执行目标

    这是我当前的 makefile CXX g CXXFLAGS Wall O3 LDFLAGS TARGET testcpp SRCS main cpp object cpp foo cpp OBJS SRCS cpp o DEPS SRCS
  • Makefile 和 .Mak 文件 + CodeBlocks 和 VStudio

    我对整个 makefile 概念有点陌生 所以我对此有一些疑问 我正在 Linux 中使用 CodeBlocks 创建一个项目 我使用一个名为 cbp2mak 的工具从 CodeBlocks 项目创建一个 make 文件 如果有人知道更好的
  • JavaScript 错误:MVC2 视图中的条件编译已关闭

    我试图在 MVC2 视图页面中单击时调用 JavaScript 函数 a href Select a JavaScript 函数 function SelectBenefit id code alert id alert code 这里 b
  • 测量进程消耗的 CPU 时钟

    我用 C 语言编写了一个程序 它是作为研究结果创建的程序 我想计算程序消耗的确切 CPU 周期 精确的循环次数 知道我怎样才能找到它吗 The valgrind tool cachegrind valgrind tool cachegrin
  • C# 获取数据表中所有重复行的计数

    我通过运行存储过程来填充数据集 并且从数据集中填充数据表 DataSet RawDataSet DataAccessHelper RunProcedure storedprocedureName this will just return
  • 如何防止 Blazor NavLink 组件的默认导航

    从 Blazor 3 1 Preview 2 开始 应该可以防止默认导航行为 https devblogs microsoft com aspnet asp net core updates in net core 3 1 preview
  • 如何在多线程应用程序中安全地填充数据并 Refresh() DataGridView?

    我的应用程序有一个 DataGridView 对象和一个 MousePos 类型的列表 MousePos 是一个自定义类 它保存鼠标 X Y 坐标 类型为 Point 和该位置的运行计数 我有一个线程 System Timers Timer
  • 当Model和ViewModel一模一样的时候怎么办?

    我想知道什么是最佳实践 我被告知要始终创建 ViewModel 并且永远不要使用核心模型类将数据传递到视图 这就说得通了 让我把事情分开 但什么是Model 和ViewModel一模一样 我应该重新创建另一个类还是只是使用它 我觉得我应该重
  • C++ 指针引用混淆

    struct leaf int data leaf l leaf r struct leaf p void tree findparent int n int found leaf parent 这是 BST 的一段代码 我想问一下 为什么
  • 如何获取带有某个属性注释的所有属性?

    我刚刚从 Roslyn 开始 我想找到所有用属性名称 OneToOne 注释的属性 我启动了 SyntaxVisualizer 并能够获取对该节点的引用 但我想知道是否有更简单的方法来实现此目的 这就是我所拥有的 var prop docu
  • 如何组合两个 lambda [重复]

    这个问题在这里已经有答案了 可能的重复 在 C 中组合两个 lambda 表达式 https stackoverflow com questions 1717444 combining two lamba expressions in c
  • winform c# 中的弹出窗口

    我正在开发一个需要弹出窗口的项目 但问题是我还希望能够通过表单设计器在此弹出窗口中添加文本框等 所以基本上我有一个按钮 当您单击它时 它将打开我在表单设计器中设计的另一个窗口 我一直在谷歌搜索 但还没有找到我需要的东西 所以我希望你们能帮助

随机推荐

  • 有没有办法在iText(Java版本)中将矩形绘制到PdfPCell中?

    我找到了一些关于如何在 iText 中绘制表单的教程 但我需要将其插入单元格中 但我不知道如何操作 感谢您对此事的考虑 由于谷歌没有对完整的代码示例给出任何好的回应 而且我花了很多时间试图弄清楚如何做到这一点 即使杰斯的解释现在看起来很清楚
  • : 包含多个单词

    我正在使用以下 jQuery var etag kate if etag length gt 0 div each function this find ul not contains etag hide this find ul cont
  • 设置水晶报表中详细信息部分的大小

    我正在使用 VS2008 中的水晶报告生成账单报告 其中 详细信息部分的大小是动态的 即取决于记录的数量 它会发生变化 因此 如果我有 10 条记录 则报告视图是整页 即 k 但是如果我在详细信息部分中有 2 条记录 则整个页脚部分将粘在详
  • Firebase 跨多个节点的数据一致性

    根据Firebase 文档数据被展平 索引用于链接树中的不同节点 users userId widgets widgetId widgets widgetId 在上面的例子中 如果用户创建了一个小部件 那么widgetid也存储在用户节点下
  • “不幸的是,应用程序名称已停止。” Android 设备或模拟器

    我是 Android 开发新手 我正在按照一些教程来创建第一个应用程序 当我想运行该应用程序时 编译器没有显示错误 但在模拟器中显示 不幸的是 取景器 blabla 已停止 代码 AndroidManifest xml 文件
  • Google App Script“异常:FILENAME.csv超出最大文件大小”的解决方案?

    我正在构建一个 Google App Maker 应用程序 它将用户上传的 Excel CSV 电子表格文件作为输入 我想过多种可能的解决方案来从该文件读取数据 但每次都遇到此错误 异常 FILENAME csv 超出最大文件大小 我尝试通
  • Hibernate:无法加载 Hibernate 配置 条目中声明的类

    我是 Hibernate 的新手 并得到了这样的堆栈跟踪 hql gt from TracksEntity 2014 04 26 21 13 45 org hibernate MappingException Unable to load
  • 将 Color32 数组转换为字节数组以通过网络发送

    我一直致力于在 Unity3D 中的 Android 设备上进行网络摄像头流媒体视频和照片捕获 我发现的大多数捕获网络摄像头源的示例都使用特定的 WebCamTexture 对象来访问设备的摄像头硬件 我目前能够捕获相机输入 但 WebCa
  • 使用 SQLiteQueryBuilder 连接两个表

    我正在尝试使用 Android SDK 的 SQLiteQueryBuilder 连接两个表 我们将它们称为 t1 和 t2 并根据每个表中的属性 例如 t1 att1 和 t2 att2 查询该表 等于某个值 在选择时我对语法有点困惑 感
  • x64 系统上的 win32 应用程序编译 0K 时出现运行时错误(dll 加载)

    我最初在win7 32位上设计了一个win32应用程序 使用VC9 0 我最近升级到win7 64位 并尝试构建 执行以前的应用程序 构建运行良好 win32 应用程序 但在运行时我收到错误 已退出 代码为 1073741701 0xc00
  • 决赛桌的合并相当于什么?

    我正在尝试使用合并来仅插入新记录 我想收集插入的新记录的 ID 以及被忽略的重复记录的 ID 这是表的创建语句 drop table SSZ ME MIS test update table create table ssz me mis
  • 如何将 TabBarView 上的滚动条“合并”到 PageView 中?

    我有一个在其主页上使用 PageView 的应用程序 今天 我被分配在其中一个页面中插入 TabBarView 问题是 当我在最后一个选项卡中滚动选项卡之间时 向左滚动将不会滚动 PageView 我需要一种方法来使页面视图在选项卡视图的开
  • 如何使用java自动从日历中选择特定日期

    我有一个案例 我必须从日历中选择 3 天回溯的日期 如何使用 selenium 自动化此案例 我正在使用 java 和 selenium 进行自动化 1 假设您可以在输入字段中写入日期 并且日历只是图标 你可以有这样的辅助方法 public
  • 如何在提交事件处理程序中获取表单值?

    我试图开始使用一个非常简单的 Google 表单 其中仅包含几个问题 只有 2 个选项和一个简短文本的多项选择 创建后 我打开脚本编辑器并输入 function onSubmit e Logger log onSubmit s JSON s
  • WP7 (Windows Phone 7) 在 XAML 或 C# 中锁定手机方向

    Windows Phone 7 是否可以手动锁定手机方向 因为我使用加速度计来处理具有固定 UI 的按钮旋转 我已经尝试过了 在 XAML 中 SupportedOrientations Landscape Orientation Land
  • 将 simple-prefs (SDK API) 与外部脚本和 postMessage 一起使用

    我正在使用 SDK 开发附加组件的新版本 针对 Gmail 并且需要为我的用户存储一些布尔首选项 为此 我想使用新的 simple prefs API 基于Mozilla 文档和这个堆栈溢出问题 我得到了一些东西 但我面临以下问题 1 我无
  • 是否可以在 vscode 中将您的代码分为 R 部分?

    有没有办法使用类似于 RStudio 的代码部分 或 MATLAB 的 在 VSCode 中 我想将我的代码分为几个部分并有选择地运行这些部分 那可能吗 Thanks Yasir 我认为您正在了解一些不同的事情 如果您想划分代码并像 jup
  • 在 R 中索引冗余命名的向量

    在 R 中 当有一个冗余命名向量时 为什么无法使用选择运算符检索命名向量中的所有元素 v lt c 1 2 3 4 5 names v lt c a b c c a v c Returns only 3 not c 3 4 看起来 R 假设
  • webGL单帧“截图”

    尝试寻找类似的东西 但我没有运气 我正在尝试打开一个新选项卡 其中包含 webgl 图像当前状态的屏幕截图 基本上 它是一个 3D 模型 能够更改显示的对象 这些对象的颜色以及背景颜色 目前 我正在使用以下内容 var screenShot
  • E*Trade API 在获取访问令牌时经常返回 HTTP 401 Unauthorized,但并非总是如此

    Summary 我编写了一个简单的 C NET Core 应用程序 使用 OAuthv1 对 E Trade API 进行身份验证 目的是获取股票报价 我能够进行身份验证并获取请求令牌 重定向到授权页面并获取验证程序字符串 但是 当我使用验