Estoy tratando de comunicarme con la API de BigQuery habilitada de mi aplicación a través del método de servidor a servidor.Solicitud de token de acceso a la cuenta de servicio de Google OAuth2 da respuesta a 'Solicitud no válida'
He marcado todas las casillas en este Google guide para construir mi JWT lo mejor que pueda en C#.
Y he codificado Base64Url todo lo que era necesario.
Sin embargo, la única respuesta que recibo de Google es una Solicitud incorrecta 400
"error" : "invalid_request"
me he asegurado de todos los siguientes de estas otras preguntas: 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
Obtengo el mismo resultado cuando utilizo Fiddler. El mensaje de error es frustrantemente carece de detalles. ¿Qué más puedo probar? Aquí está mi código:
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;
}
}
No estoy encontrando nada descaradamente obvio, y he recorrido cada línea de su código. Una cosa podría ser que esté codificando el tipo de concesión en su diccionario, y FormUrlEncodededContent puede terminar doble codificación. Por lo tanto, probaría "urna: ietf: params: oauth: grant-type: jwt-bearer" en su lugar. –
Parece que HttpClient pertenece a una versión reciente de .NET framework, así que lo estoy instalando y probando el código directamente también. También me comuniqué internamente con algunas personas que podrían ayudar. –