代码之家  ›  专栏  ›  技术社区  ›  Nick

Google OAuth2服务帐户访问令牌请求给出“无效请求”响应

  •  12
  • Nick  · 技术社区  · 14 年前

    我正在尝试通过服务器到服务器方法与我的应用程序启用的BigQuery API通信。

    我已经勾选了所有的方框 Google guide 为了在C#中尽我所能构建我的JWT。

    我已经用Base64Url编码了所有必要的东西。

    然而,我从谷歌得到的唯一回应是一个400错误的请求

    "error" : "invalid_request"
    

    我已经从这些其他SO问题中确定了以下所有内容:

    当我使用Fiddler时,我得到了相同的结果。令人沮丧的是,错误消息缺乏详细信息!我还能尝试什么?!这是我的代码:

    class Program
    {
        static void Main(string[] args)
        {
            // certificate
            var certificate = new X509Certificate2(@"<Path to my certificate>.p12", "notasecret");
    
            // header
            var header = new { typ = "JWT", alg = "RS256" };
    
            // claimset
            var times = GetExpiryAndIssueDate();
            var claimset = new
            {
                iss = "<email address of the client id of my app>",
                scope = "https://www.googleapis.com/auth/bigquery",
                aud = "https://accounts.google.com/o/oauth2/token",
                iat = times[0],
                exp = times[1],
            };
    
            // encoded header
            var headerSerialized = JsonConvert.SerializeObject(header);
            var headerBytes = Encoding.UTF8.GetBytes(headerSerialized);
            var headerEncoded = Base64UrlEncode(headerBytes);
    
            // encoded claimset
            var claimsetSerialized = JsonConvert.SerializeObject(claimset);
            var claimsetBytes = Encoding.UTF8.GetBytes(claimsetSerialized);
            var claimsetEncoded = Base64UrlEncode(claimsetBytes);
    
            // input
            var input = headerEncoded + "." + claimsetEncoded;
            var inputBytes = Encoding.UTF8.GetBytes(input);
    
            // signiture
            var rsa = certificate.PrivateKey as RSACryptoServiceProvider;
            var cspParam = new CspParameters
            {
                KeyContainerName = rsa.CspKeyContainerInfo.KeyContainerName,
                KeyNumber = rsa.CspKeyContainerInfo.KeyNumber == KeyNumber.Exchange ? 1 : 2
            };
            var aescsp = new RSACryptoServiceProvider(cspParam) { PersistKeyInCsp = false };
            var signatureBytes = aescsp.SignData(inputBytes, "SHA256");
            var signatureEncoded = Base64UrlEncode(signatureBytes);
    
            // jwt
            var jwt = headerEncoded + "." + claimsetEncoded + "." + signatureEncoded;
    
            Console.WriteLine(jwt);
    
            var client = new HttpClient();
            var uri = "https://accounts.google.com/o/oauth2/token";
            var post = new Dictionary<string, string>
            {
                {"assertion", jwt},
                {"grant_type", "urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer"}
            };
            var content = new FormUrlEncodedContent(post);
            var result = client.PostAsync(uri, content).Result;
    
            Console.WriteLine(result);
            Console.WriteLine(result.Content.ReadAsStringAsync().Result);
            Console.ReadLine();
        }
    
        private static int[] GetExpiryAndIssueDate()
        {
            var utc0 = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc);
            var issueTime = DateTime.Now;
    
            var iat = (int)issueTime.Subtract(utc0).TotalSeconds;
            var exp = (int)issueTime.AddMinutes(55).Subtract(utc0).TotalSeconds;
    
            return new[]{iat, exp};
        }
    
        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;
        }
    }
    
    2 回复  |  直到 9 年前
        1
  •  12
  •   Ryan Boyd    14 年前

    看来我在上面评论中的猜测是正确的。我通过更改使您的代码正常工作:

    "urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer"

    收件人:

    "urn:ietf:params:oauth:grant-type:jwt-bearer"

    看起来你不小心对它进行了双重编码。

    我现在得到的回复如下:

    {
      "access_token" : "1/_5pUwJZs9a545HSeXXXXXuNGITp1XtHhZXXxxyyaacqkbc",
      "token_type" : "Bearer",
      "expires_in" : 3600
    }
    

    编辑说明:请确保服务器上的日期/时间/时区/dst配置正确。即使时钟关闭几秒钟,也会导致 invalid_grant 错误 http://www.time.gov 将给出美国政府的官方时间,包括UTC。

        2
  •  6
  •   Rolfvm    14 年前

    请确保在GetExpiryAndIssueDate方法中使用DateTime.UtcNow而不是DateTime.Now。