2012-08-13 13 views
12

Próbuję się komunikować z włączoną funkcją API BigQuery mojej aplikacji za pośrednictwem metody serwer-serwer.Prośba o dostęp do konta dostępu do usługi Google OAuth2 daje odpowiedź "Nieprawidłowe żądanie"

Zaznaczam wszystkie pola w tym Google guide, aby skonstruować moje JWT najlepiej jak potrafię w języku C#.

I już Base64Url zakodował wszystko, co było konieczne.

Jednak jedyną odpowiedzią uzyskać od Google jest 400 Bad Request

"error" : "invalid_request" 

Zrobiłem pewien wszystkie następujące z nich inny SO pytania:

Otrzymuję ten sam wynik, gdy używam Skrzypka. Komunikat o błędzie jest frustrująco brakuje w szczegółach! Co jeszcze mogę spróbować ?! Oto mój kod:

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

Nie znajduję niczego jawnie oczywistego i przeszedłem przez każdy wiersz kodu. Jedna rzecz może polegać na tym, że kodujesz typ przydziału w słowniku, a FormUrlEncodededContent może skończyć się podwójnym kodowaniem go. Tak, chciałbym spróbować "urn: ietf: params: oauth: grant-type: jwt-bearer" zamiast tego. –

+1

Wygląda na to, że HttpClient pochodzi z najnowszej wersji platformy .NET, więc instaluję to i wypróbowuję kod bezpośrednio. Osiągnąłem również wewnętrznie kilka osób, które mogą pomóc. –

Odpowiedz

12

Wygląda na to, że moje przypuszczenie w powyższym komentarzu było poprawne. Mam swój kod działa poprzez zmianę:

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

do:

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

Wygląda jakbyś przypadkiem podwójnego kodowania go.

teraz uzyskać odpowiedź, która wygląda mniej więcej tak:

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

Redakcja Uwaga: upewnij się, że masz prawidłową datę konfiguracji strefy czasowej DST/czasu/czas/na serwerze. Wyłączenie zegara nawet o kilka sekund spowoduje błąd o numerze invalid_grant. http://www.time.gov wyda oficjalny czas z rządu USA, w tym w UTC.

5

Należy używać DateTime.UtcNow zamiast DateTime.Now w metodzie GetExpiryAndIssueDate.

Powiązane problemy