2012-08-13 8 views
12

Sto provando a comunicare con l'API BigQuery abilitata della mia app tramite il metodo server-server.Google OAuth2 La richiesta del token di accesso all'account del servizio dà la risposta "Richiesta non valida"

Ho spuntato tutte le caselle su questo Google guide per costruire il mio JWT come meglio posso in C#.

E ho Base64Url codificato tutto ciò che era necessario.

Tuttavia, l'unica risposta che ricevo da Google è una 400 Richiesta

"error" : "invalid_request" 

ho fatto in modo di tutti i seguenti da queste altre domande SO:

Ottengo lo stesso risultato quando utilizzo Fiddler. Il messaggio di errore è frustrantemente privo di dettagli! Cos'altro posso provare ?! Qui è il mio codice:

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

Non sto trovando nulla di evidentemente ovvio, e ho superato ogni riga del codice. Una cosa potrebbe essere che stai codificando il tipo di concessione nel tuo dizionario, e FormUrlEncodededContent potrebbe finire con la doppia codifica. Quindi, proverei "urn: ietf: params: oauth: grant-type: jwt-bearer". –

+1

Sembra che HttpClient provenga da una versione di framework .NET molto recente, quindi lo sto installando e provando direttamente il codice. Ho anche contattato internamente alcune persone che potrebbero essere in grado di aiutare. –

risposta

12

Sembra che la mia ipotesi nel commento di cui sopra è stato corretto. Ho ricevuto il tuo codice di lavoro modificando:

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

a:

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

Sembra che tu fossi accidentalmente doppio encoding.

io ora ottenere una risposta che sembra qualcosa di simile:

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

A cura Nota: assicurarsi di avere il/ora/fuso orario configurazione corretta data/ora legale sul server. Disattivare l'orologio di alcuni secondi comporterà l'errore invalid_grant. http://www.time.gov assegnerà l'ora ufficiale dal governo USA, incluso in UTC.

5

Assicurarsi di utilizzare DateTime.UtcNow anziché DateTime.Now nel metodo GetExpiryAndIssueDate.