2012-08-13 9 views
12

Ich versuche, über die Server-Server-Methode mit der aktivierten BigQuery-API meiner App zu kommunizieren.Google OAuth2-Dienstkonto-Zugriffstokenanforderung gibt die Antwort "Ungültige Anforderung"

Ich habe alle Felder auf diesem Google guide für die Konstruktion meiner JWT so gut wie möglich in C# angekreuzt.

Und ich habe Base64Url codiert alles, was notwendig war.

jedoch die einzige Antwort, die ich von Google erhalten, ist eine 400 Bad Request

"error" : "invalid_request" 

ich gemacht habe aus diesen anderen SO Fragen von allen folgenden sicher:

Ich bekomme das gleiche Ergebnis, wenn ich Fiddler verwende. Die Fehlermeldung fehlt frustrierend im Detail! Was kann ich sonst noch versuchen ?! Hier ist mein 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; 
    } 
} 
+0

Ich finde nichts offensichtlich offensichtlich, und ich bin durch jede Zeile Ihres Codes gegangen. Eine Sache könnte sein, dass Sie den Grant-Typ in Ihrem Wörterbuch codieren, und FormUrlEncodedContent könnte es am Ende doppelt codieren. Also würde ich stattdessen "urn: ietf: params: oauth: grant-type: jwt-bearer" versuchen. –

+1

Sieht so aus, als ob HttpClient von einer sehr aktuellen Version von .NET Framework stammt, also installiere ich das und probiere den Code auch direkt aus. Ich habe auch intern ein paar Leute angesprochen, die vielleicht helfen könnten. –

Antwort

12

Sieht aus wie meine Schätzung in den obigen Kommentar richtig war. Ich habe den Code arbeiten, indem:

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

zu:

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

Sieht aus wie Sie versehentlich doppelt kodiert waren es.

ich jetzt eine Antwort, die wie etwas aussieht:

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

Edited Hinweis: Bitte stellen Sie sicher, das richtige Datum/Zeit/Zeitzone/dst-Konfiguration auf Ihrem Server. Wenn die Uhr nur um einige Sekunden ausgeschaltet ist, wird ein invalid_grant Fehler angezeigt. http://www.time.gov gibt die offizielle Zeit von der US-Regierung, einschließlich in UTC.

5

Achten Sie darauf, DateTime.UtcNow anstelle von DateTime.Now in der GetExpiryAndIssueDate-Methode zu verwenden.

Verwandte Themen