Skip to content

Commit abc0d76

Browse files
committed
Merge pull request #272 from trilobyte/fix_oauth1_get_parameter_signature
Fix oauth1 get parameter signature
2 parents ea14345 + 3904047 commit abc0d76

File tree

4 files changed

+134
-9
lines changed

4 files changed

+134
-9
lines changed
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace RestSharp.IntegrationTests.Models
2+
{
3+
/// <summary>
4+
/// Model for used by the LinkedIN integration tests. <see cref="oAuth1Tests.Can_Retrieve_Member_Profile_Field_Field_Selector_From_LinkedIN"/>.
5+
/// </summary>
6+
public class LinkedINMemberProfile
7+
{
8+
public string Id { get; set; }
9+
public string FirstName { get; set; }
10+
public string LastName { get; set; }
11+
}
12+
}

RestSharp.IntegrationTests/RestSharp.IntegrationTests.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@
6565
<Compile Include="Helpers\Extensions.cs" />
6666
<Compile Include="FileTests.cs" />
6767
<Compile Include="Helpers\Handlers.cs" />
68+
<Compile Include="Models\LinkedINMemberProfile.cs" />
6869
<Compile Include="oAuth1Tests.cs" />
6970
<Compile Include="Properties\AssemblyInfo.cs" />
7071
<Compile Include="CompressionTests.cs" />

RestSharp.IntegrationTests/oAuth1Tests.cs

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22
using System.Linq;
33
using System.Xml.Serialization;
44
using RestSharp.Authenticators.OAuth;
5+
using RestSharp.IntegrationTests.Models;
56
using Xunit;
67
using System.Net;
78
using RestSharp.Contrib;
@@ -186,5 +187,107 @@ public void Use_RFC_3986_Encoding_For_Auth_Signature_Base()
186187
// assert
187188
Assert.Equal( "%3B%2F%3F%3A%40%26%3D%2B%24%2C%21%2A%27%28%29", escapedString );
188189
}
190+
191+
[Fact( Skip = "Provide your own consumer key/secret before running" )]
192+
public void Can_Authenticate_LinkedIN_With_OAuth()
193+
{
194+
const string consumerKey = "TODO_CONSUMER_KEY_HERE";
195+
const string consumerSecret = "TODO_CONSUMER_SECRET_HERE";
196+
197+
// request token
198+
var client = new RestClient {
199+
BaseUrl = "https://api.linkedin.com/uas/oauth",
200+
Authenticator = OAuth1Authenticator.ForRequestToken( consumerKey, consumerSecret, "http://localhost" )
201+
};
202+
var requestTokenRequest = new RestRequest( "requestToken" );
203+
var requestTokenResponse = client.Execute( requestTokenRequest );
204+
Assert.NotNull( requestTokenResponse );
205+
Assert.Equal( HttpStatusCode.OK, requestTokenResponse.StatusCode );
206+
var requestTokenResponseParameters = HttpUtility.ParseQueryString( requestTokenResponse.Content );
207+
var requestToken = requestTokenResponseParameters[ "oauth_token" ];
208+
var requestSecret = requestTokenResponseParameters[ "oauth_token_secret" ];
209+
Assert.NotNull( requestToken );
210+
Assert.NotNull( requestSecret );
211+
212+
// redirect user
213+
requestTokenRequest = new RestRequest( "authenticate?oauth_token=" + requestToken );
214+
var redirectUri = client.BuildUri( requestTokenRequest );
215+
Process.Start( redirectUri.ToString() );
216+
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
217+
if ( !Debugger.IsAttached )
218+
{
219+
Debugger.Launch();
220+
}
221+
Debugger.Break();
222+
223+
// get the access token
224+
var requestTokenQueryParameters = HttpUtility.ParseQueryString( new Uri( requestUrl ).Query );
225+
var requestVerifier = requestTokenQueryParameters[ "oauth_verifier" ];
226+
client.Authenticator = OAuth1Authenticator.ForAccessToken( consumerKey, consumerSecret, requestToken, requestSecret, requestVerifier );
227+
var requestAccessTokenRequest = new RestRequest( "accessToken" );
228+
var requestActionTokenResponse = client.Execute( requestAccessTokenRequest );
229+
Assert.NotNull( requestActionTokenResponse );
230+
Assert.Equal( HttpStatusCode.OK, requestActionTokenResponse.StatusCode );
231+
var requestActionTokenResponseParameters = HttpUtility.ParseQueryString( requestActionTokenResponse.Content );
232+
var accessToken = requestActionTokenResponseParameters[ "oauth_token" ];
233+
var accessSecret = requestActionTokenResponseParameters[ "oauth_token_secret" ];
234+
Assert.NotNull( accessToken );
235+
Assert.NotNull( accessSecret );
236+
}
237+
238+
[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" )]
239+
public void Can_Retrieve_Member_Profile_Field_Field_Selector_From_LinkedIN()
240+
{
241+
const string consumerKey = "TODO_CONSUMER_KEY_HERE";
242+
const string consumerSecret = "TODO_CONSUMER_SECRET_HERE";
243+
const string accessToken = "TODO_ACCES_TOKEN_HERE";
244+
const string accessSecret = "TODO_ACCES_SECRET_HERE";
245+
246+
// arrange
247+
var client = new RestClient {
248+
BaseUrl = "http://api.linkedin.com/v1",
249+
Authenticator = OAuth1Authenticator.ForProtectedResource( consumerKey, consumerSecret, accessToken, accessSecret )
250+
};
251+
var request = new RestRequest( "people/~:(id,first-name,last-name)" );
252+
253+
// act
254+
var response = client.Execute< LinkedINMemberProfile >( request );
255+
256+
// assert
257+
Assert.NotNull( response );
258+
Assert.Equal( HttpStatusCode.OK, response.StatusCode );
259+
Assert.NotNull( response.Data );
260+
Assert.NotNull( response.Data.Id );
261+
Assert.NotNull( response.Data.FirstName );
262+
Assert.NotNull( response.Data.LastName );
263+
}
264+
265+
[Fact( Skip = "Provide your own consumer key/secret before running" )]
266+
public void Can_Query_Vimeo()
267+
{
268+
const string consumerKey = "TODO_CONSUMER_KEY_HERE";
269+
const string consumerSecret = "TODO_CONSUMER_SECRET_HERE";
270+
271+
// arrange
272+
var client = new RestClient {
273+
BaseUrl = "http://vimeo.com/api/rest/v2",
274+
Authenticator = OAuth1Authenticator.ForRequestToken( consumerKey, consumerSecret )
275+
};
276+
var request = new RestRequest();
277+
request.AddParameter( "format", "json" );
278+
request.AddParameter( "method", "vimeo.videos.search" );
279+
request.AddParameter( "query", "weather" );
280+
request.AddParameter( "full_response", 1 );
281+
282+
// act
283+
var response = client.Execute( request );
284+
285+
// assert
286+
Assert.NotNull( response );
287+
Assert.Equal( HttpStatusCode.OK, response.StatusCode );
288+
Assert.NotNull( response.Content );
289+
Assert.False( response.Content.Contains( "\"stat\":\"fail\"" ) );
290+
Assert.True( response.Content.Contains( "\"stat\":\"ok\"" ) );
291+
}
189292
}
190293
}

RestSharp/Authenticators/OAuth1Authenticator.cs

Lines changed: 18 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515

1616
namespace RestSharp.Authenticators
1717
{
18+
/// <seealso href="http://tools.ietf.org/html/rfc5849"/>
1819
public class OAuth1Authenticator : IAuthenticator
1920
{
2021
public virtual string Realm { get; set; }
@@ -150,19 +151,27 @@ public void Authenticate(IRestClient client, IRestRequest request)
150151
private void AddOAuthData(IRestClient client, IRestRequest request, OAuthWorkflow workflow)
151152
{
152153
var url = client.BuildUri(request).ToString();
154+
var queryStringStart = url.IndexOf('?');
155+
if (queryStringStart != -1)
156+
url = url.Substring(0, queryStringStart);
153157

154158
OAuthWebQueryInfo oauth;
155159
var method = request.Method.ToString().ToUpperInvariant();
156160

157161
var parameters = new WebParameterCollection();
158162

159-
// for non-GET style requests make sure params are part of oauth signature
160-
if (request.Method != Method.GET && request.Method != Method.DELETE)
163+
// include all GET and POST parameters before generating the signature
164+
// according to the RFC 5849 - The OAuth 1.0 Protocol
165+
// http://tools.ietf.org/html/rfc5849#section-3.4.1
166+
// if this change causes trouble we need to introduce a flag indicating the specific OAuth implementation level,
167+
// or implement a seperate class for each OAuth version
168+
foreach (var p in client.DefaultParameters.Where(p => p.Type == ParameterType.GetOrPost))
161169
{
162-
foreach (var p in request.Parameters.Where(p => p.Type == ParameterType.GetOrPost))
163-
{
164-
parameters.Add(new WebPair(p.Name, p.Value.ToString()));
165-
}
170+
parameters.Add( new WebPair( p.Name, p.Value.ToString() ) );
171+
}
172+
foreach (var p in request.Parameters.Where(p => p.Type == ParameterType.GetOrPost))
173+
{
174+
parameters.Add(new WebPair(p.Name, p.Value.ToString()));
166175
}
167176

168177
switch (Type)
@@ -193,10 +202,10 @@ private void AddOAuthData(IRestClient client, IRestRequest request, OAuthWorkflo
193202
request.AddHeader("Authorization", GetAuthorizationHeader(parameters));
194203
break;
195204
case OAuthParameterHandling.UrlOrPostParameters:
196-
parameters.Add("oauth_signature", HttpUtility.UrlDecode(oauth.Signature));
197-
foreach (var parameter in parameters)
205+
parameters.Add("oauth_signature", oauth.Signature);
206+
foreach (var parameter in parameters.Where(parameter => !parameter.Name.IsNullOrBlank() && parameter.Name.StartsWith("oauth_")))
198207
{
199-
request.AddParameter(parameter.Name, parameter.Value);
208+
request.AddParameter(parameter.Name, HttpUtility.UrlDecode(parameter.Value));
200209
}
201210
break;
202211
default:

0 commit comments

Comments
 (0)