Google OAuth2服务帐户访问令牌请求给出'无效请求'响应
问题描述:
我试图通过服务器到服务器方法与我的应用程序的启用BigQuery API进行通信。Google OAuth2服务帐户访问令牌请求给出'无效请求'响应
我已经勾选了此Google guide上的所有框,用于构建我的JWT,尽我所能在C#中完成。
而且我用Base64Url编码了所有必需的东西。
不过,我从谷歌得到的唯一回应是400错误的请求
"error" : "invalid_request"
我所有这些其他SO下列问题的确信:
- The signature is properly encrypted using RSA and SHA256
- I am using POST and using application/x-www-form-urlencoded content type
- Escaped all the backslashes in the claim set
- Tried various grant_type and assertion values in the POST data
当我使用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;
}
}
答
看起来像我在上面评论中的猜测是正确的。我收到了你的代码通过改变工作:
"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。
答
确保在GetExpiryAndIssueDate方法中使用DateTime.UtcNow而不是DateTime.Now。
我没有发现任何明显的东西,而且我遍历了代码的每一行。有一件事可能是你在字典中编码授权类型,而FormUrlEncodededContent最终可能会对它进行双重编码。所以,我会尝试“urn:ietf:params:oauth:grant-type:jwt-bearer”。 – 2012-08-13 19:14:04
看起来HttpClient来自一个非常新的.NET框架版本,所以我正在安装它,并直接尝试代码。我也向内部的几个人提供了帮助。 – 2012-08-13 19:15:02