You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

.NET 7调用Twitter API V2遇Unsupported Authentication问题求助

排查Twitter API V2 OAuth 1.0a认证错误(Unsupported Authentication)

核心问题分析及修复方案

  • 时间戳格式错误:
    代码中时间戳使用TotalSeconds直接转字符串,会生成带小数的数值(如1699999999.123),但Twitter要求Unix时间戳必须是整数秒数。这会导致签名验证失败,触发认证错误。
    修复:将时间戳转换为长整型:

    var timeStamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
    
  • Authorization头缺失oauth_token参数:
    签名生成逻辑中包含了oauth_token参数,但实际发送的Authorization头里没有这个字段,导致签名与请求参数不匹配,Twitter无法识别认证方式。
    修复:在Authorization头中添加oauth_token:

    request.AddHeader("Authorization", $"OAuth oauth_consumer_key=\"{consumerKey}\",oauth_token=\"{accessToken}\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"{timeStamp}\",oauth_nonce=\"{nonce}\",oauth_version=\"1.0\",oauth_signature=\"{signature}\"");
    
  • 不必要的消息编码:
    对message做了HttpUtility.UrlEncode,但请求体是JSON格式,JSON会自动处理转义,提前编码会导致推文内容出现%20这类编码字符,影响内容正确性。
    修复:移除encodedMessage,直接使用原始message:

    request.AddJsonBody(new { text = message });
    

修改后的完整代码示例

public async Task<ActionResult> SendTweet([FromQuery] string accessToken, [FromQuery] string secret, [FromQuery] string message)
{
    string consumerKey = "YOUR_CONSUMER_KEY";
    string consumerSecret = "YOUR_CONSUMER_SECRET";
    var nonce = Guid.NewGuid().ToString();
    // 修正时间戳为整数秒数
    var timeStamp = (long)(DateTime.UtcNow - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)).TotalSeconds;
    string signature = GetSignatureBaseString("https://api.twitter.com/2/tweets", timeStamp.ToString(), nonce, consumerKey, accessToken, consumerSecret, secret);
    signature = HttpUtility.UrlEncode(signature);
    
    var client = new RestClient("https://api.twitter.com/2/tweets");
    var request = new RestRequest(Method.Post);
    request.AddHeader("content-type", "application/json");
    // 修复Authorization头,添加oauth_token参数
    request.AddHeader("Authorization", $"OAuth oauth_consumer_key=\"{consumerKey}\",oauth_token=\"{accessToken}\",oauth_signature_method=\"HMAC-SHA1\",oauth_timestamp=\"{timeStamp}\",oauth_nonce=\"{nonce}\",oauth_version=\"1.0\",oauth_signature=\"{signature}\"");
    // 移除不必要的UrlEncode,直接使用原始message
    request.AddJsonBody(new { text = message });
    
    RestResponse response = await client.ExecuteAsync(request);
    Console.WriteLine(response.Content);
    return Ok(response.Content);
}

private static string GetSignatureBaseString(string url, string timeStamp,
    string nonce, string consumerKey, string accessToken, string consumerSecret, string secret)
{
    string signatureBaseString = "POST&";
    signatureBaseString += Uri.EscapeDataString(url);
    signatureBaseString += "&";

    var parameters = new SortedDictionary<string, string>
    {
        {"oauth_consumer_key", consumerKey},
        {"oauth_token", accessToken},
        {"oauth_signature_method", "HMAC-SHA1"},
        {"oauth_timestamp", timeStamp},
        {"oauth_nonce", nonce},
        {"oauth_version", "1.0"}
    };

    bool first = true;
    StringBuilder keyValuePairs = new StringBuilder();
    foreach (var kvp in parameters)
    {
        if (!first)
            keyValuePairs.Append("&");
        keyValuePairs.Append($"{Uri.EscapeDataString(kvp.Key)}={Uri.EscapeDataString(kvp.Value)}");
        first = false;
    }

    signatureBaseString += Uri.EscapeDataString(keyValuePairs.ToString());

    string signatureKey = $"{consumerSecret}&{secret}";
    using var hmac = new HMACSHA1(Encoding.ASCII.GetBytes(signatureKey));
    byte[] signatureBytes = hmac.ComputeHash(Encoding.ASCII.GetBytes(signatureBaseString));
    return Convert.ToBase64String(signatureBytes);
}

额外验证点

  1. 确认accessToken和secret参数从QueryString正确传递,没有被篡改或编码错误。
  2. 检查消费者密钥和令牌是否与Postman中使用的完全一致,无空格或拼写错误。

内容的提问来源于stack exchange,提问作者Álex

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.04 10:10:32