2012-08-13 2 views
12

Я пытаюсь связаться с включенным API-интерфейсом BigQuery моего приложения с помощью метода server-server.Запрос на токен доступа к учетной записи Google OAuth2 дает ответ «Недействительный запрос»

Я пометил все поля на этом Google guide для создания моего JWT как можно лучше на C#.

И у меня Base64Url закодировал все, что было необходимо.

Однако, единственный ответ, который я получаю от Google является 400 Bad Request

"error" : "invalid_request" 

Я убедился всех следующих из этих других SO вопросы:

Я получаю тот же результат, когда использую Fiddler. Сообщение об ошибке разочаровывает в деталях! Что еще я могу попробовать ?! Вот мой код:

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

Я не нахожу ничего очевидного, и я прошел через каждую строку вашего кода. Одно может быть, что вы кодируете тип гранта в своем словаре, а FormUrlEncodededContent может в итоге дважды закодировать его. Итак, я бы попробовал вместо этого «urn: ietf: params: oauth: grant-type: jwt-bearer». –

+1

Похож, что HttpClient - это совсем недавно релиз .NET framework, поэтому я устанавливаю это и сам тестирую код. Я также обратился к нескольким людям, которые могли бы помочь. –

ответ

12

Похоже, что моя догадка в комментарии выше была верна. Я получил код работает за счет изменения:

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

к:

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

Похоже, вы случайно дважды кодирующая его.

я теперь получить ответ, который выглядит примерно так:

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

Edited Примечание: Пожалуйста, убедитесь, что есть правильный/время/часовой пояс конфигурации даты/Dst на сервере. Выключение часов на несколько секунд приведет к ошибке invalid_grant. http://www.time.gov даст официальное время от правительства США, в том числе в UTC.

5

Обязательно используйте DateTime.UtcNow вместо DateTime.Now в методе GetExpiryAndIssueDate.

Смежные вопросы