Google OAuth2 服务帐户访问令牌请求提供“无效请求"响应 [英] Google OAuth2 Service Account Access Token Request gives 'Invalid Request' Response
问题描述
我正在尝试通过服务器到服务器方法与我的应用启用的 BigQuery API 通信.
I'm trying to communicate with my app's enabled BigQuery API via the server to server method.
我已勾选此 Google 指南上的所有方框以构建我的 JWT尽我所能使用 C#.
I've ticked all the boxes on this Google guide for constructing my JWT as best I can in C#.
我已经用 Base64Url 编码了所有必要的东西.
And I've Base64Url encoded everything that was necessary.
然而,我从谷歌得到的唯一回应是 400 Bad Request
However, the only response I get from google is a 400 Bad Request
"error" : "invalid_request"
我已经从这些其他 SO 问题中确定了以下所有内容:
I've made sure of all of the following from these other SO questions:
- 签名已正确加密使用 RSA 和 SHA256
- 我正在使用 POST 并使用 application/x-www-form-urlencoded 内容类型
- 转义声明集中的所有反斜杠
- 在 POST 数据中尝试了各种 grant_type 和 assertion 值
当我使用 Fiddler 时,我得到了相同的结果.错误信息令人沮丧地缺乏详细信息!我还能尝试什么?!这是我的代码:
I get the same result when I use Fiddler. The error message is frustratingly lacking in detail! What else can I try?! Here's my code:
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;
}
}
推荐答案
看起来我在上面评论中的猜测是正确的.我通过更改使您的代码正常工作:
Looks like my guess in the comment above was correct. I got your code working by changing:
"urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer"
到:
"urn:ietf:params:oauth:grant-type:jwt-bearer"
看起来您不小心对其进行了双重编码.
Looks like you were accidentally double-encoding it.
我现在得到的回复类似于:
I now get a response which looks something like:
{
"access_token" : "1/_5pUwJZs9a545HSeXXXXXuNGITp1XtHhZXXxxyyaacqkbc",
"token_type" : "Bearer",
"expires_in" : 3600
}
编辑注意:请确保您的服务器上的日期/时间/时区/dst 配置正确.即使时钟关闭几秒钟也会导致 invalid_grant
错误.http://www.time.gov 将给出美国政府的官方时间,包括 UTC.
Edited Note: please make sure to have the correct date/time/timezone/dst configuration on your server. Having the clock off by even a few seconds will result in an invalid_grant
error. http://www.time.gov will give the official time from the US govt, including in UTC.
这篇关于Google OAuth2 服务帐户访问令牌请求提供“无效请求"响应的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!