From e51b5a477991a61addaecb906e5dbce0978658b7 Mon Sep 17 00:00:00 2001 From: Bert Willems Date: Wed, 9 May 2012 15:17:31 +0200 Subject: [PATCH 1/8] added integration test for LinkedIN --- .../Models/LinkedINMemberProfile.cs | 12 +++ .../RestSharp.IntegrationTests.csproj | 1 + RestSharp.IntegrationTests/oAuth1Tests.cs | 75 +++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 RestSharp.IntegrationTests/Models/LinkedINMemberProfile.cs diff --git a/RestSharp.IntegrationTests/Models/LinkedINMemberProfile.cs b/RestSharp.IntegrationTests/Models/LinkedINMemberProfile.cs new file mode 100644 index 000000000..2446aed7f --- /dev/null +++ b/RestSharp.IntegrationTests/Models/LinkedINMemberProfile.cs @@ -0,0 +1,12 @@ +namespace RestSharp.IntegrationTests.Models +{ + /// + /// Model for used by the LinkedIN integration tests. . + /// + public class LinkedINMemberProfile + { + public string Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + } +} \ No newline at end of file diff --git a/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj b/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj index a0b615298..b0e8a3dfd 100644 --- a/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj +++ b/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj @@ -65,6 +65,7 @@ + diff --git a/RestSharp.IntegrationTests/oAuth1Tests.cs b/RestSharp.IntegrationTests/oAuth1Tests.cs index d9002c680..8906dc609 100644 --- a/RestSharp.IntegrationTests/oAuth1Tests.cs +++ b/RestSharp.IntegrationTests/oAuth1Tests.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Xml.Serialization; using RestSharp.Authenticators.OAuth; +using RestSharp.IntegrationTests.Models; using Xunit; using System.Net; using RestSharp.Contrib; @@ -186,5 +187,79 @@ public void Use_RFC_3986_Encoding_For_Auth_Signature_Base() // assert Assert.Equal( "%3B%2F%3F%3A%40%26%3D%2B%24%2C%21%2A%27%28%29", escapedString ); } + + [Fact( Skip = "Provide your own consumer key/secret before running" )] + public void Can_Authenticate_LinkedIN_With_OAuth() + { + const string consumerKey = "TODO_CONSUMER_KEY_HERE"; + const string consumerSecret = "TODO_CONSUMER_SECRET_HERE"; + + // request token + var client = new RestClient { + BaseUrl = "https://api.linkedin.com/uas/oauth", + Authenticator = OAuth1Authenticator.ForRequestToken( consumerKey, consumerSecret, "http://localhost" ) + }; + var requestTokenRequest = new RestRequest( "requestToken" ); + var requestTokenResponse = client.Execute( requestTokenRequest ); + Assert.NotNull( requestTokenResponse ); + Assert.Equal( HttpStatusCode.OK, requestTokenResponse.StatusCode ); + var requestTokenResponseParameters = HttpUtility.ParseQueryString( requestTokenResponse.Content ); + var requestToken = requestTokenResponseParameters[ "oauth_token" ]; + var requestSecret = requestTokenResponseParameters[ "oauth_token_secret" ]; + Assert.NotNull( requestToken ); + Assert.NotNull( requestSecret ); + + // redirect user + requestTokenRequest = new RestRequest( "authenticate?oauth_token=" + requestToken ); + var redirectUri = client.BuildUri( requestTokenRequest ); + Process.Start( redirectUri.ToString() ); + var requestUrl = "TODO: put browser URL here"; // replace this via the debugger with the return url from LinkedIN. Simply copy it from the opened browser + if ( !Debugger.IsAttached ) + { + Debugger.Launch(); + } + Debugger.Break(); + + // get the access token + var requestTokenQueryParameters = HttpUtility.ParseQueryString( new Uri( requestUrl ).Query ); + var requestVerifier = requestTokenQueryParameters[ "oauth_verifier" ]; + client.Authenticator = OAuth1Authenticator.ForAccessToken( consumerKey, consumerSecret, requestToken, requestSecret, requestVerifier ); + var requestAccessTokenRequest = new RestRequest( "accessToken" ); + var requestActionTokenResponse = client.Execute( requestAccessTokenRequest ); + Assert.NotNull( requestActionTokenResponse ); + Assert.Equal( HttpStatusCode.OK, requestActionTokenResponse.StatusCode ); + var requestActionTokenResponseParameters = HttpUtility.ParseQueryString( requestActionTokenResponse.Content ); + var accessToken = requestActionTokenResponseParameters[ "oauth_token" ]; + var accessSecret = requestActionTokenResponseParameters[ "oauth_token_secret" ]; + Assert.NotNull( accessToken ); + Assert.NotNull( accessSecret ); + } + + [Fact( Skip = "Provide your own consumer key/secret/accessToken/accessSecret before running. You can retrieve the access token/secret by running the LinkedIN oAuth test" )] + public void Can_Retrieve_Member_Profile_Field_Field_Selector_From_LinkedIN() + { + const string consumerKey = "TODO_CONSUMER_KEY_HERE"; + const string consumerSecret = "TODO_CONSUMER_SECRET_HERE"; + const string accessToken = "TODO_ACCES_TOKEN_HERE"; + const string accessSecret = "TODO_ACCES_SECRET_HERE"; + + // arrange + var client = new RestClient { + BaseUrl = "http://api.linkedin.com/v1", + Authenticator = OAuth1Authenticator.ForProtectedResource( consumerKey, consumerSecret, accessToken, accessSecret ) + }; + var request = new RestRequest( "people/~:(id,first-name,last-name)" ); + + // act + var response = client.Execute< LinkedINMemberProfile >( request ); + + // assert + Assert.NotNull( response ); + Assert.Equal( HttpStatusCode.OK, response.StatusCode ); + Assert.NotNull( response.Data ); + Assert.NotNull( response.Data.Id ); + Assert.NotNull( response.Data.FirstName ); + Assert.NotNull( response.Data.LastName ); + } } } From 82092b4122fc8bb760c23c079ab0a68fc70c6243 Mon Sep 17 00:00:00 2001 From: Bert Willems Date: Wed, 9 May 2012 15:53:59 +0200 Subject: [PATCH 2/8] fix for #271 Invalid OAuth1 signature for GET request --- RestSharp.IntegrationTests/oAuth1Tests.cs | 28 +++++++++++++++++++ .../Authenticators/OAuth1Authenticator.cs | 14 ++++++---- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/RestSharp.IntegrationTests/oAuth1Tests.cs b/RestSharp.IntegrationTests/oAuth1Tests.cs index 8906dc609..ca734e50b 100644 --- a/RestSharp.IntegrationTests/oAuth1Tests.cs +++ b/RestSharp.IntegrationTests/oAuth1Tests.cs @@ -261,5 +261,33 @@ public void Can_Retrieve_Member_Profile_Field_Field_Selector_From_LinkedIN() Assert.NotNull( response.Data.FirstName ); Assert.NotNull( response.Data.LastName ); } + + [Fact( Skip = "Provide your own consumer key/secret before running" )] + public void Can_Query_Vimeo() + { + const string consumerKey = "TODO_CONSUMER_KEY_HERE"; + const string consumerSecret = "TODO_CONSUMER_SECRET_HERE"; + + // arrange + var client = new RestClient { + BaseUrl = "http://vimeo.com/api/rest/v2", + Authenticator = OAuth1Authenticator.ForRequestToken( consumerKey, consumerSecret ) + }; + var request = new RestRequest(); + request.AddParameter( "format", "json" ); + request.AddParameter( "method", "vimeo.videos.search" ); + request.AddParameter( "query", "weather" ); + request.AddParameter( "full_response", 1 ); + + // act + var response = client.Execute( request ); + + // assert + Assert.NotNull( response ); + Assert.Equal( HttpStatusCode.OK, response.StatusCode ); + Assert.NotNull( response.Content ); + Assert.False( response.Content.Contains( "\"stat\":\"fail\"" ) ); + Assert.True( response.Content.Contains( "\"stat\":\"ok\"" ) ); + } } } diff --git a/RestSharp/Authenticators/OAuth1Authenticator.cs b/RestSharp/Authenticators/OAuth1Authenticator.cs index 09723bce8..e55c9b2a9 100644 --- a/RestSharp/Authenticators/OAuth1Authenticator.cs +++ b/RestSharp/Authenticators/OAuth1Authenticator.cs @@ -13,6 +13,7 @@ namespace RestSharp.Authenticators { + /// public class OAuth1Authenticator : IAuthenticator { public virtual string Realm { get; set; } @@ -154,13 +155,14 @@ private void AddOAuthData(IRestClient client, IRestRequest request, OAuthWorkflo var parameters = new WebParameterCollection(); - // for non-GET style requests make sure params are part of oauth signature - if (request.Method != Method.GET && request.Method != Method.DELETE) + // include all GET and POST parameters before generating the signature + // according to the RFC 5849 - The OAuth 1.0 Protocol + // http://tools.ietf.org/html/rfc5849#section-3.4.1 + // if this change causes trouble we need to introduce a flag indicating the specific OAuth implementation level, + // or implement a seperate class for each OAuth version + foreach (var p in request.Parameters.Where(p => p.Type == ParameterType.GetOrPost)) { - foreach (var p in request.Parameters.Where(p => p.Type == ParameterType.GetOrPost)) - { - parameters.Add(new WebPair(p.Name, p.Value.ToString())); - } + parameters.Add(new WebPair(p.Name, p.Value.ToString())); } switch (Type) From 0afda7d3b005b645ca6a3716ec81895a8f99177e Mon Sep 17 00:00:00 2001 From: Bert Willems Date: Wed, 9 May 2012 15:17:31 +0200 Subject: [PATCH 3/8] added integration test for LinkedIN --- .../Models/LinkedINMemberProfile.cs | 12 +++ .../RestSharp.IntegrationTests.csproj | 1 + RestSharp.IntegrationTests/oAuth1Tests.cs | 75 +++++++++++++++++++ 3 files changed, 88 insertions(+) create mode 100644 RestSharp.IntegrationTests/Models/LinkedINMemberProfile.cs diff --git a/RestSharp.IntegrationTests/Models/LinkedINMemberProfile.cs b/RestSharp.IntegrationTests/Models/LinkedINMemberProfile.cs new file mode 100644 index 000000000..2446aed7f --- /dev/null +++ b/RestSharp.IntegrationTests/Models/LinkedINMemberProfile.cs @@ -0,0 +1,12 @@ +namespace RestSharp.IntegrationTests.Models +{ + /// + /// Model for used by the LinkedIN integration tests. . + /// + public class LinkedINMemberProfile + { + public string Id { get; set; } + public string FirstName { get; set; } + public string LastName { get; set; } + } +} \ No newline at end of file diff --git a/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj b/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj index a0b615298..b0e8a3dfd 100644 --- a/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj +++ b/RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj @@ -65,6 +65,7 @@ + diff --git a/RestSharp.IntegrationTests/oAuth1Tests.cs b/RestSharp.IntegrationTests/oAuth1Tests.cs index d9002c680..8906dc609 100644 --- a/RestSharp.IntegrationTests/oAuth1Tests.cs +++ b/RestSharp.IntegrationTests/oAuth1Tests.cs @@ -2,6 +2,7 @@ using System.Linq; using System.Xml.Serialization; using RestSharp.Authenticators.OAuth; +using RestSharp.IntegrationTests.Models; using Xunit; using System.Net; using RestSharp.Contrib; @@ -186,5 +187,79 @@ public void Use_RFC_3986_Encoding_For_Auth_Signature_Base() // assert Assert.Equal( "%3B%2F%3F%3A%40%26%3D%2B%24%2C%21%2A%27%28%29", escapedString ); } + + [Fact( Skip = "Provide your own consumer key/secret before running" )] + public void Can_Authenticate_LinkedIN_With_OAuth() + { + const string consumerKey = "TODO_CONSUMER_KEY_HERE"; + const string consumerSecret = "TODO_CONSUMER_SECRET_HERE"; + + // request token + var client = new RestClient { + BaseUrl = "https://api.linkedin.com/uas/oauth", + Authenticator = OAuth1Authenticator.ForRequestToken( consumerKey, consumerSecret, "http://localhost" ) + }; + var requestTokenRequest = new RestRequest( "requestToken" ); + var requestTokenResponse = client.Execute( requestTokenRequest ); + Assert.NotNull( requestTokenResponse ); + Assert.Equal( HttpStatusCode.OK, requestTokenResponse.StatusCode ); + var requestTokenResponseParameters = HttpUtility.ParseQueryString( requestTokenResponse.Content ); + var requestToken = requestTokenResponseParameters[ "oauth_token" ]; + var requestSecret = requestTokenResponseParameters[ "oauth_token_secret" ]; + Assert.NotNull( requestToken ); + Assert.NotNull( requestSecret ); + + // redirect user + requestTokenRequest = new RestRequest( "authenticate?oauth_token=" + requestToken ); + var redirectUri = client.BuildUri( requestTokenRequest ); + Process.Start( redirectUri.ToString() ); + var requestUrl = "TODO: put browser URL here"; // replace this via the debugger with the return url from LinkedIN. Simply copy it from the opened browser + if ( !Debugger.IsAttached ) + { + Debugger.Launch(); + } + Debugger.Break(); + + // get the access token + var requestTokenQueryParameters = HttpUtility.ParseQueryString( new Uri( requestUrl ).Query ); + var requestVerifier = requestTokenQueryParameters[ "oauth_verifier" ]; + client.Authenticator = OAuth1Authenticator.ForAccessToken( consumerKey, consumerSecret, requestToken, requestSecret, requestVerifier ); + var requestAccessTokenRequest = new RestRequest( "accessToken" ); + var requestActionTokenResponse = client.Execute( requestAccessTokenRequest ); + Assert.NotNull( requestActionTokenResponse ); + Assert.Equal( HttpStatusCode.OK, requestActionTokenResponse.StatusCode ); + var requestActionTokenResponseParameters = HttpUtility.ParseQueryString( requestActionTokenResponse.Content ); + var accessToken = requestActionTokenResponseParameters[ "oauth_token" ]; + var accessSecret = requestActionTokenResponseParameters[ "oauth_token_secret" ]; + Assert.NotNull( accessToken ); + Assert.NotNull( accessSecret ); + } + + [Fact( Skip = "Provide your own consumer key/secret/accessToken/accessSecret before running. You can retrieve the access token/secret by running the LinkedIN oAuth test" )] + public void Can_Retrieve_Member_Profile_Field_Field_Selector_From_LinkedIN() + { + const string consumerKey = "TODO_CONSUMER_KEY_HERE"; + const string consumerSecret = "TODO_CONSUMER_SECRET_HERE"; + const string accessToken = "TODO_ACCES_TOKEN_HERE"; + const string accessSecret = "TODO_ACCES_SECRET_HERE"; + + // arrange + var client = new RestClient { + BaseUrl = "http://api.linkedin.com/v1", + Authenticator = OAuth1Authenticator.ForProtectedResource( consumerKey, consumerSecret, accessToken, accessSecret ) + }; + var request = new RestRequest( "people/~:(id,first-name,last-name)" ); + + // act + var response = client.Execute< LinkedINMemberProfile >( request ); + + // assert + Assert.NotNull( response ); + Assert.Equal( HttpStatusCode.OK, response.StatusCode ); + Assert.NotNull( response.Data ); + Assert.NotNull( response.Data.Id ); + Assert.NotNull( response.Data.FirstName ); + Assert.NotNull( response.Data.LastName ); + } } } From 4929b9ac3b1d95d358c27f43db371faf4fcce2f1 Mon Sep 17 00:00:00 2001 From: Bert Willems Date: Wed, 9 May 2012 15:53:59 +0200 Subject: [PATCH 4/8] fix for #271 Invalid OAuth1 signature for GET request --- RestSharp.IntegrationTests/oAuth1Tests.cs | 28 +++++++++++++++++++ .../Authenticators/OAuth1Authenticator.cs | 14 ++++++---- 2 files changed, 36 insertions(+), 6 deletions(-) diff --git a/RestSharp.IntegrationTests/oAuth1Tests.cs b/RestSharp.IntegrationTests/oAuth1Tests.cs index 8906dc609..ca734e50b 100644 --- a/RestSharp.IntegrationTests/oAuth1Tests.cs +++ b/RestSharp.IntegrationTests/oAuth1Tests.cs @@ -261,5 +261,33 @@ public void Can_Retrieve_Member_Profile_Field_Field_Selector_From_LinkedIN() Assert.NotNull( response.Data.FirstName ); Assert.NotNull( response.Data.LastName ); } + + [Fact( Skip = "Provide your own consumer key/secret before running" )] + public void Can_Query_Vimeo() + { + const string consumerKey = "TODO_CONSUMER_KEY_HERE"; + const string consumerSecret = "TODO_CONSUMER_SECRET_HERE"; + + // arrange + var client = new RestClient { + BaseUrl = "http://vimeo.com/api/rest/v2", + Authenticator = OAuth1Authenticator.ForRequestToken( consumerKey, consumerSecret ) + }; + var request = new RestRequest(); + request.AddParameter( "format", "json" ); + request.AddParameter( "method", "vimeo.videos.search" ); + request.AddParameter( "query", "weather" ); + request.AddParameter( "full_response", 1 ); + + // act + var response = client.Execute( request ); + + // assert + Assert.NotNull( response ); + Assert.Equal( HttpStatusCode.OK, response.StatusCode ); + Assert.NotNull( response.Content ); + Assert.False( response.Content.Contains( "\"stat\":\"fail\"" ) ); + Assert.True( response.Content.Contains( "\"stat\":\"ok\"" ) ); + } } } diff --git a/RestSharp/Authenticators/OAuth1Authenticator.cs b/RestSharp/Authenticators/OAuth1Authenticator.cs index a2f19f829..94805a5ab 100644 --- a/RestSharp/Authenticators/OAuth1Authenticator.cs +++ b/RestSharp/Authenticators/OAuth1Authenticator.cs @@ -15,6 +15,7 @@ namespace RestSharp.Authenticators { + /// public class OAuth1Authenticator : IAuthenticator { public virtual string Realm { get; set; } @@ -156,13 +157,14 @@ private void AddOAuthData(IRestClient client, IRestRequest request, OAuthWorkflo var parameters = new WebParameterCollection(); - // for non-GET style requests make sure params are part of oauth signature - if (request.Method != Method.GET && request.Method != Method.DELETE) + // include all GET and POST parameters before generating the signature + // according to the RFC 5849 - The OAuth 1.0 Protocol + // http://tools.ietf.org/html/rfc5849#section-3.4.1 + // if this change causes trouble we need to introduce a flag indicating the specific OAuth implementation level, + // or implement a seperate class for each OAuth version + foreach (var p in request.Parameters.Where(p => p.Type == ParameterType.GetOrPost)) { - foreach (var p in request.Parameters.Where(p => p.Type == ParameterType.GetOrPost)) - { - parameters.Add(new WebPair(p.Name, p.Value.ToString())); - } + parameters.Add(new WebPair(p.Name, p.Value.ToString())); } switch (Type) From 78bd2b634b000239c466d70270adb16b6721d606 Mon Sep 17 00:00:00 2001 From: Bert Willems Date: Tue, 5 Jun 2012 14:39:59 +0200 Subject: [PATCH 5/8] * only add oauth parameters --- RestSharp/Authenticators/OAuth1Authenticator.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RestSharp/Authenticators/OAuth1Authenticator.cs b/RestSharp/Authenticators/OAuth1Authenticator.cs index 94805a5ab..4021b846e 100644 --- a/RestSharp/Authenticators/OAuth1Authenticator.cs +++ b/RestSharp/Authenticators/OAuth1Authenticator.cs @@ -196,7 +196,7 @@ private void AddOAuthData(IRestClient client, IRestRequest request, OAuthWorkflo break; case OAuthParameterHandling.UrlOrPostParameters: parameters.Add("oauth_signature", HttpUtility.UrlDecode(oauth.Signature)); - foreach (var parameter in parameters) + foreach (var parameter in parameters.Where(parameter => !parameter.Name.IsNullOrBlank() && parameter.Name.StartsWith("oauth_"))) { request.AddParameter(parameter.Name, parameter.Value); } From f36b40a60d93ec786967ecc17a5281167e966479 Mon Sep 17 00:00:00 2001 From: Bert Willems Date: Tue, 5 Jun 2012 15:42:43 +0200 Subject: [PATCH 6/8] * decode all oauh parameters --- RestSharp/Authenticators/OAuth1Authenticator.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RestSharp/Authenticators/OAuth1Authenticator.cs b/RestSharp/Authenticators/OAuth1Authenticator.cs index 4021b846e..cb4c485f9 100644 --- a/RestSharp/Authenticators/OAuth1Authenticator.cs +++ b/RestSharp/Authenticators/OAuth1Authenticator.cs @@ -195,10 +195,10 @@ private void AddOAuthData(IRestClient client, IRestRequest request, OAuthWorkflo request.AddHeader("Authorization", GetAuthorizationHeader(parameters)); break; case OAuthParameterHandling.UrlOrPostParameters: - parameters.Add("oauth_signature", HttpUtility.UrlDecode(oauth.Signature)); + parameters.Add("oauth_signature", oauth.Signature); foreach (var parameter in parameters.Where(parameter => !parameter.Name.IsNullOrBlank() && parameter.Name.StartsWith("oauth_"))) { - request.AddParameter(parameter.Name, parameter.Value); + request.AddParameter(parameter.Name, HttpUtility.UrlDecode(parameter.Value)); } break; default: From 22152fd46ec7db7c71dc9d0baab14b644f075b7d Mon Sep 17 00:00:00 2001 From: Bert Willems Date: Tue, 5 Jun 2012 15:56:55 +0200 Subject: [PATCH 7/8] * add default parameters from the client --- RestSharp/Authenticators/OAuth1Authenticator.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RestSharp/Authenticators/OAuth1Authenticator.cs b/RestSharp/Authenticators/OAuth1Authenticator.cs index cb4c485f9..e20833ade 100644 --- a/RestSharp/Authenticators/OAuth1Authenticator.cs +++ b/RestSharp/Authenticators/OAuth1Authenticator.cs @@ -162,6 +162,10 @@ private void AddOAuthData(IRestClient client, IRestRequest request, OAuthWorkflo // http://tools.ietf.org/html/rfc5849#section-3.4.1 // if this change causes trouble we need to introduce a flag indicating the specific OAuth implementation level, // or implement a seperate class for each OAuth version + foreach (var p in client.DefaultParameters.Where(p => p.Type == ParameterType.GetOrPost)) + { + parameters.Add( new WebPair( p.Name, p.Value.ToString() ) ); + } foreach (var p in request.Parameters.Where(p => p.Type == ParameterType.GetOrPost)) { parameters.Add(new WebPair(p.Name, p.Value.ToString())); From 3904047f0933acb69a83cb4fd7dbb6c6aa3eb130 Mon Sep 17 00:00:00 2001 From: Bert Willems Date: Tue, 5 Jun 2012 16:55:42 +0200 Subject: [PATCH 8/8] * do no add query parameters twice --- RestSharp/Authenticators/OAuth1Authenticator.cs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/RestSharp/Authenticators/OAuth1Authenticator.cs b/RestSharp/Authenticators/OAuth1Authenticator.cs index e20833ade..faac64aaa 100644 --- a/RestSharp/Authenticators/OAuth1Authenticator.cs +++ b/RestSharp/Authenticators/OAuth1Authenticator.cs @@ -151,6 +151,9 @@ public void Authenticate(IRestClient client, IRestRequest request) private void AddOAuthData(IRestClient client, IRestRequest request, OAuthWorkflow workflow) { var url = client.BuildUri(request).ToString(); + var queryStringStart = url.IndexOf('?'); + if (queryStringStart != -1) + url = url.Substring(0, queryStringStart); OAuthWebQueryInfo oauth; var method = request.Method.ToString().ToUpperInvariant();