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..ca734e50b 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,107 @@ 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 ); + } + + [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..faac64aaa 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; } @@ -150,19 +151,27 @@ 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(); 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 client.DefaultParameters.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() ) ); + } + foreach (var p in request.Parameters.Where(p => p.Type == ParameterType.GetOrPost)) + { + parameters.Add(new WebPair(p.Name, p.Value.ToString())); } switch (Type) @@ -193,10 +202,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)); - foreach (var parameter in parameters) + 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: