Freebase oAuth2 错误请求(400).Net

Posted

技术标签:

【中文标题】Freebase oAuth2 错误请求(400).Net【英文标题】:Freebase oAuth2 Bad Request(400) .Net 【发布时间】:2015-02-25 17:47:22 【问题描述】:

我阅读了教程 https://developers.google.com/accounts/docs/OAuth2ServiceAccount

并尝试使用他们的示例,但不断收到 400 个错误请求。 这是我的代码:

 ClaimSet cs = new ClaimSet()
        
            aud = "https://www.googleapis.com/oauth2/v3/token",
            iss = "1070248278615-hoq0meaunarl9hj8t9klg4gqkohlme9u@developer.gserviceaccount.com",
            exp = GetTime(DateTime.UtcNow.AddHours(1)).ToString(),
            iat = GetTime(DateTime.UtcNow).ToString(),
            scope = "https://www.googleapis.com/auth/freebase"
        ;

        //get the signed JWT
        var signedJwt = JsonWebToken.Encode(cs);           


public static string Encode(object payload, JwtHashAlgorithm algorithm = JwtHashAlgorithm.RS256)
        
            return Encode(payload, Encoding.UTF8.GetBytes(PrivateKey), algorithm);
        

    public static string Encode(object payload, byte[] keyBytes, JwtHashAlgorithm algorithm)
    
        var segments = new List<string>();
        var header = new  alg = algorithm.ToString(), typ = "JWT" ;

        byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header, Formatting.None));
        byte[] payloadBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload, Formatting.None));

        segments.Add(Base64UrlEncode(headerBytes));
        segments.Add(Base64UrlEncode(payloadBytes));

        var stringToSign = string.Join(".", segments);

        var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);

        byte[] signature = HashAlgorithms[algorithm](keyBytes, bytesToSign);
        segments.Add(Base64UrlEncode(signature));

        return string.Join(".", segments.ToArray());
    
        using (var wb = new WebClient())
        
            var url = "https://www.googleapis.com/oauth2/v3/token/";
            wb.Headers.Add("Content-Type", "application/x-www-form-urlencoded");
            var data2 = new NameValueCollection();
            data2["grant_type"] = "urn:ietf:params:oauth:grant-type:jwt-bearer";
            data2["assertion"] = signedJwt;
            var response2 = wb.UploadValues(url, "POST", data2);
        

现在在获得访问令牌后,我尝试写入 freebase: 使用以下教程,我看到我应该 Get 动词: https://developers.google.com/accounts/docs/OAuth2ServiceAccount#creatinganaccount

    var url = "https://www.googleapis.com/freebase/v1/mqlwrite";
                wb.QueryString.Add("lang", "/lang/en");
                wb.QueryString.Add("query", "%5B%7B%0A%20%20%22mid%22%3A%20%22%2Fm%2F011840dm%22%2C%0A%20%20%22%2Fcommon%2Ftopic%2Ftopic_equivalent_webpage%22%3A%20%7B%0A%20%20%20%20%22connect%22%3A%20%22insert%22%2C%0A%20%20%20%20%22value%22%3A%20%22http%3A%2F%2Fwww.imdb.com%2Fname%2Fnm4963898%2F%22%0A%20%20%7D%0A%7D%5D");
                wb.Headers.Add("Authorization", "Bearer " + accesstoken);
                var ResponseBytes = wb.DownloadString(url);

感谢帮助:)

【问题讨论】:

【参考方案1】:

您在 POST 数据中包含 Content-Type,但它应该作为 HTTP 标头的一部分显示,如下所示:

wb.Headers.Add("Content-Type","application/x-www-form-urlencoded");

然而,使用 UploadValues 这将是默认设置,除非被覆盖。

除此之外,UploadValues 会自动对您的值进行 URL 编码,因此您应该以原始形式呈现它们;所以对于授权类型,这意味着:

data2["grant_type"] = "urn:ietf:params:oauth:grant-type:jwt-bearer";

编辑1: 此外,您的 JWT 使用了错误的 aud 声明,因为它设置为 https://accounts.google.com/o/oauth2/token 而不是 https://www.googleapis.com/oauth2/v3/token,并且自 2012 年 7 月 2 日起过期。

编辑2: 您还必须发布到不带斜杠的 URL 并正确获取 iatexp 时间戳。使用Newtonsoft.Json成功测试代码:

public class GoogleServiceAccountBearerJWTSample

    private static string Base64UrlEncode(byte[] input)
    
        var output = Convert.ToBase64String(input);
        output = output.Split('=')[0]; // Remove any trailing '='s
        output = output.Replace('+', '-'); // 62nd char of encoding
        output = output.Replace('/', '_'); // 63rd char of encoding
        return output;
    

    public static string Encode(object payload, AsymmetricAlgorithm rsa) 
        var segments = new List<string>();
        var header = new  alg = "RS256", typ = "JWT" ;
        byte[] headerBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(header, Formatting.None));
        byte[] payloadBytes = Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(payload, Formatting.None));
        segments.Add(Base64UrlEncode(headerBytes));
        segments.Add(Base64UrlEncode(payloadBytes));
        var stringToSign = string.Join(".", segments.ToArray());
        var bytesToSign = Encoding.UTF8.GetBytes(stringToSign);

        // VARIANT A - should work on non-SHA256 enabled systems
        var rs = rsa as RSACryptoServiceProvider;
        var cspParam = new CspParameters
        
            KeyContainerName = rs.CspKeyContainerInfo.KeyContainerName,
            KeyNumber = rs.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2
        ;
        var aescsp = new RSACryptoServiceProvider(cspParam)  PersistKeyInCsp = false ;
        var signature = aescsp.SignData(bytesToSign, "SHA256");
        // END OF VARIANT A

        // VARIANT B - works on FIPS SHA256 enabled systems
        // var pkcs1 = new RSAPKCS1SignatureFormatter(rsa);
        // pkcs1.SetHashAlgorithm("SHA256");
        // var signature = pkcs1.CreateSignature(new SHA256Managed().ComputeHash(bytesToSign));
        // END OF VARIANT B

        segments.Add(Base64UrlEncode(signature));
        return string.Join(".", segments.ToArray());
   

   public static void Main()
       
        var utc0 = new DateTime(1970,1,1,0,0,0,0, DateTimeKind.Utc);
        var issueTime = DateTime.UtcNow;

        var iat = (int)issueTime.Subtract(utc0).TotalSeconds;
        var exp = (int)issueTime.AddMinutes(55).Subtract(utc0).TotalSeconds; // Expiration time is up to 1 hour, but lets play on safe side

        var payload = new 
            iss = "xxxxxxxxxxxxxxxxxx@developer.gserviceaccount.com",
            aud = "https://www.googleapis.com/oauth2/v3/token",
            scope = "https://www.googleapis.com/auth/freebase",
            exp = exp,
            iat = iat
        ;

        var certificate = new X509Certificate2("google-client.p12", "notasecret");
        var signedJwt = Encode(payload, certificate.PrivateKey);

        //System.Console.WriteLine(signedJwt);

        using (var wb = new WebClient())
        
            var url = "https://www.googleapis.com/oauth2/v3/token";
            var data2 = new NameValueCollection();
            data2["grant_type"] = "urn:ietf:params:oauth:grant-type:jwt-bearer";
            data2["assertion"] = signedJwt;
            var response2 = wb.UploadValues(url, "POST", data2);
            System.Console.WriteLine(Encoding.UTF8.GetString(response2));
        
    

【讨论】:

感谢您的回复。我按照您的建议更改了我的代码(如您在上面看到的),但不幸的是仍然收到相同的“400 Bad Request”错误。我做错了什么? 见附录回答 @Hans Z. 你确定吗? JWT 取自 [link]developers.google.com/accounts/docs/OAuth2ServiceAccount 中的示例,我看到他们确实使用 [link] googleapis.com/oauth2/v3/token 你看到其他了吗? 好的,这就是您问题的根本原因:您不能采用 a JWT(如 Google 示例中的 JWT)并展示它...您需要在 Google API 控制台中生成您的自己的客户端和关联的私钥,并使用该信息(电子邮件 + 私钥)创建和签署 您自己的 JWT。创建您自己的客户端的过程在developers.google.com/accounts/docs/… 中有很好的描述。 @mashtagidi:您可以为您的问题创建一个单独的问题 @HansZ。我在 Google API 控制台中生成了我自己的客户端和关联的私钥,并使用这些信息(电子邮件 + 私钥)来创建和签署我自己的 JWT。但不断收到相同的错误“400 Bad Request”。我在我的问题中添加了我的代码。

以上是关于Freebase oAuth2 错误请求(400).Net的主要内容,如果未能解决你的问题,请参考以下文章

对 oAuth2 Keycloak 服务器的 HTTPS 请求在 Xamarin 中向我返回 400 'Bad Request'

Oauth2:远程服务器返回错误:(400) Bad Request

来自客户端 javascript 的错误 400 Cognito /oauth2/token 端点

spring-security oauth2 REST 服务器在错误凭据(400)错误响应中不需要的 Stacktrace

Google Drive API 推送通知订阅 400“解析错误”

无法让 Google App Engine OAuth2-sample(用于 Python)工作 - 400 错误:redirect_uri_mismatch [重复]