2012-08-13 16 views
12

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

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; 
    } 
} 
+0

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. –

+1

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. –

Respuesta

12

Parece que mi conjetura en el comentario anterior era correcta. Tengo tu código de trabajo cambiando:

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

a:

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

Parece que se encontraba accidentalmente doble que lo codifica.

ahora tengo una respuesta que será similar a:

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

Nota Editado: por favor asegúrese de tener la correcta configuración de fecha/hora/zona horaria/DST en su servidor. Si el reloj se apaga unos segundos, se producirá un error de invalid_grant. http://www.time.gov dará la hora oficial del gobierno de los EE. UU., Incluso en UTC.

5

Asegúrese de utilizar DateTime.UtcNow en lugar de DateTime.Now en el método GetExpiryAndIssueDate.

Cuestiones relacionadas