2012-08-13 6 views
12

サーバーからサーバーへのメソッドを使用して、アプリの有効なBigQuery APIと通信しようとしています。Google OAuth2サービスアカウントアクセストークンリクエストで「無効なリクエスト」応答が返される

このGoogle guideのすべてのボックスに、C#で可能な限り最高の状態でJWTを構築するためのチェックを入れました。

そして、私はBase64Urlに必要なすべてをエンコードしました。

しかし、私はGoogleからの取得のみ応答が、私はこれらの他のSOの質問から、次のすべてのことを確認作った

"error" : "invalid_request" 

400不正な要求です:

私は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

私は大変明白なことは何も見つけていません。コードの各行を踏んだことがあります。 1つは、辞書の許可タイプをエンコードしている可能性があり、FormUrlEncodededContentがそれをダブルエンコードする可能性があります。ですから、私は代わりに "urn:ietf:params:oauth:grant-type:jwt-bearer"を試してみます。 –

+1

HttpClientは非常に最近の.NETフレームワークリリースのものですから、私はそれをインストールし、コードを直接試してみます。私はまた、内部的にいくつかの人々に手伝ってくれました。 –

答えて

12

上記のコメントの私の推測のように見えます。あなたが誤って二重エンコードしていたように見えます

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

:に

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

を:私は変更することによって、作業コードを得ました。

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

編集注:

私は今のようになります応答を取得し、サーバー上の正しい日付/時間/タイムゾーン/ DSTの設定を持っていることを確認してください。時計を数秒でもオフにすると、invalid_grantエラーが発生します。 http://www.time.govは、UTCを含む米国政府からの正式な時間を与えるでしょう。

5

GetExpiryAndIssueDateメソッドでDateTime.Nowの代わりにDateTime.UtcNowを必ず使用してください。

関連する問題