2012-08-13 115 views
12

我试图通过服务器到服务器方法与我的应用程序的启用BigQuery API进行通信。Google OAuth2服务帐户访问令牌请求给出'无效请求'响应

我已经勾选了此Google guide上的所有框,用于构建我的JWT,尽我所能在C#中完成。

而且我用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; 
    } 
} 
+0

我没有发现任何明显的东西,而且我遍历了代码的每一行。有一件事可能是你在字典中编码授权类型,而FormUrlEncodededContent最终可能会对它进行双重编码。所以,我会尝试“urn:ietf:params:oauth:grant-type:jwt-bearer”。 – 2012-08-13 19:14:04

+1

看起来HttpClient来自一个非常新的.NET框架版本,所以我正在安装它,并直接尝试代码。我也向内部的几个人提供了帮助。 – 2012-08-13 19:15:02

回答

12

看起来像我在上面评论中的猜测是正确的。我收到了你的代码通过改变工作:

"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。

5

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

相关问题