Added hammock project to debug streaming issues
[pithos-ms-client] / trunk / hammock / src / net35 / Hammock / Authentication / OAuth / OAuthWorkflow.cs
1 using System;
2 using System.Collections.Generic;
3 using Hammock.Extensions;
4 using Hammock.Web;
5 #if !SILVERLIGHT && !MonoTouch &&!NETCF
6 using System.Web;
7 #endif
8
9 #if ClientProfiles
10 using System.Compat.Web;
11 #endif
12
13 namespace Hammock.Authentication.OAuth
14 {
15     /// <summary>
16     /// A class to encapsulate OAuth authentication flow.
17     /// <seealso cref="http://oauth.net/core/1.0#anchor9"/>
18     /// </summary>
19     public class OAuthWorkflow
20     {
21         public virtual string Version { get; set; }
22         public virtual string ConsumerKey { get; set; }
23         public virtual string ConsumerSecret { get; set; }
24         public virtual string Token { get; set; }
25         public virtual string TokenSecret { get; set; }
26         public virtual string CallbackUrl { get; set; }
27         public virtual string Verifier { get; set; }
28         public virtual string SessionHandle { get; set; }
29         
30         public virtual OAuthSignatureMethod SignatureMethod { get; set; }
31         public virtual OAuthSignatureTreatment SignatureTreatment { get; set; }
32         public virtual OAuthParameterHandling ParameterHandling { get; set; }
33
34         public virtual string ClientUsername { get; set; }
35         public virtual string ClientPassword { get; set; }
36
37         /// <seealso cref="http://oauth.net/core/1.0#request_urls"/>
38         public virtual string RequestTokenUrl { get; set; }
39
40         /// <seealso cref="http://oauth.net/core/1.0#request_urls"/>
41         public virtual string AccessTokenUrl { get; set; }
42
43         /// <seealso cref="http://oauth.net/core/1.0#request_urls"/>
44         public virtual string AuthorizationUrl { get; set; }
45
46         /// <summary>
47         /// Generates a <see cref="OAuthWebQueryInfo"/> instance to pass to an
48         /// <see cref="OAuthWebQuery" /> for the purpose of requesting an
49         /// unauthorized request token.
50         /// </summary>
51         /// <param name="method">The HTTP method for the intended request</param>
52         /// <seealso cref="http://oauth.net/core/1.0#anchor9"/>
53         /// <returns></returns>
54         public OAuthWebQueryInfo BuildRequestTokenInfo(WebMethod method)
55         {
56             return BuildRequestTokenInfo(method, null);
57         }
58
59         public OAuthWorkflow()
60         {
61             
62         }
63
64         /// <summary>
65         /// Creates a new instance of <see cref="OAuthWorkflow" /> using
66         /// an <see cref="OAuthCredentials" /> instance.
67         /// </summary>
68         /// <param name="credentials">The credentials to copy</param>
69         public OAuthWorkflow(OAuthCredentials credentials)
70         {
71             InitializeFromCredentials(credentials);
72         }
73
74         private void InitializeFromCredentials(OAuthCredentials credentials)
75         {
76             ConsumerKey = credentials.ConsumerKey;
77             ConsumerSecret = credentials.ConsumerSecret;
78             ParameterHandling = credentials.ParameterHandling;
79             SignatureMethod = credentials.SignatureMethod;
80             SignatureTreatment = credentials.SignatureTreatment;
81             Token = credentials.Token;
82             TokenSecret = credentials.TokenSecret;
83             Verifier = credentials.Verifier;
84             ClientUsername = credentials.ClientUsername;
85             ClientPassword = credentials.ClientPassword;
86             CallbackUrl = credentials.CallbackUrl;
87             Version = credentials.Version;
88             SessionHandle = credentials.SessionHandle;
89         }
90
91         /// <summary>
92         /// Generates a <see cref="OAuthWebQueryInfo"/> instance to pass to an
93         /// <see cref="OAuthWebQuery" /> for the purpose of requesting an
94         /// unauthorized request token.
95         /// </summary>
96         /// <param name="method">The HTTP method for the intended request</param>
97         /// <param name="parameters">Any existing, non-OAuth query parameters desired in the request</param>
98         /// <seealso cref="http://oauth.net/core/1.0#anchor9"/>
99         /// <returns></returns>
100         public virtual OAuthWebQueryInfo BuildRequestTokenInfo(WebMethod method, WebParameterCollection parameters)
101         {
102             ValidateTokenRequestState();
103
104             if (parameters == null)
105             {
106                 parameters = new WebParameterCollection();
107             }
108
109             var timestamp = OAuthTools.GetTimestamp();
110             var nonce = OAuthTools.GetNonce();
111
112             AddAuthParameters(parameters, timestamp, nonce);
113
114             var signatureBase = OAuthTools.ConcatenateRequestElements(method, RequestTokenUrl, parameters);
115             var signature = OAuthTools.GetSignature(SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret);
116
117             var info = new OAuthWebQueryInfo
118                            {
119                                WebMethod = method,
120                                ParameterHandling = ParameterHandling,
121                                ConsumerKey = ConsumerKey,
122                                SignatureMethod = SignatureMethod.ToRequestValue(),
123                                SignatureTreatment = SignatureTreatment,
124                                Signature = signature,
125                                Timestamp = timestamp,
126                                Nonce = nonce,
127                                Version = Version,
128                                Callback = OAuthTools.UrlEncodeRelaxed(CallbackUrl ?? ""),
129                                UserAgent = "Hammock",
130                                TokenSecret = TokenSecret,
131                                ConsumerSecret = ConsumerSecret
132                            };
133
134             return info;
135         }
136
137         /// <summary>
138         /// Generates a <see cref="OAuthWebQueryInfo"/> instance to pass to an
139         /// <see cref="OAuthWebQuery" /> for the purpose of exchanging a request token
140         /// for an access token authorized by the user at the Service Provider site.
141         /// </summary>
142         /// <param name="method">The HTTP method for the intended request</param>
143         /// <seealso cref="http://oauth.net/core/1.0#anchor9"/>
144         public virtual OAuthWebQueryInfo BuildAccessTokenInfo(WebMethod method)
145         {
146             return BuildAccessTokenInfo(method, null);
147         }
148
149         /// <summary>
150         /// Generates a <see cref="OAuthWebQueryInfo"/> instance to pass to an
151         /// <see cref="OAuthWebQuery" /> for the purpose of exchanging a request token
152         /// for an access token authorized by the user at the Service Provider site.
153         /// </summary>
154         /// <param name="method">The HTTP method for the intended request</param>
155         /// <seealso cref="http://oauth.net/core/1.0#anchor9"/>
156         /// <param name="parameters">Any existing, non-OAuth query parameters desired in the request</param>
157         public virtual OAuthWebQueryInfo BuildAccessTokenInfo(WebMethod method, WebParameterCollection parameters)
158         {
159             ValidateAccessRequestState();
160
161             if (parameters == null)
162             {
163                 parameters = new WebParameterCollection();
164             }
165
166             var uri = new Uri(AccessTokenUrl);
167             var timestamp = OAuthTools.GetTimestamp();
168             var nonce = OAuthTools.GetNonce();
169
170             AddAuthParameters(parameters, timestamp, nonce);
171
172             var signatureBase = OAuthTools.ConcatenateRequestElements(method, uri.ToString(), parameters);
173             var signature = OAuthTools.GetSignature(SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret, TokenSecret);
174
175             var info = new OAuthWebQueryInfo
176                            {
177                                WebMethod = method,
178                                ParameterHandling = ParameterHandling,
179                                ConsumerKey = ConsumerKey,
180                                Token = Token,
181                                SignatureMethod = SignatureMethod.ToRequestValue(),
182                                SignatureTreatment = SignatureTreatment,
183                                Signature = signature,
184                                Timestamp = timestamp,
185                                Nonce = nonce,
186                                Version = Version,
187                                Verifier = Verifier,
188                                Callback = CallbackUrl,
189                                UserAgent = "Hammock",
190                                TokenSecret = TokenSecret,
191                                ConsumerSecret = ConsumerSecret,
192                            };
193
194             return info;
195         }
196
197         /// <summary>
198         /// Generates a <see cref="OAuthWebQueryInfo"/> instance to pass to an
199         /// <see cref="OAuthWebQuery" /> for the purpose of exchanging user credentials
200         /// for an access token authorized by the user at the Service Provider site.
201         /// </summary>
202         /// <param name="method">The HTTP method for the intended request</param>
203         /// <seealso cref="http://tools.ietf.org/html/draft-dehora-farrell-oauth-accesstoken-creds-00#section-4"/>
204         /// <param name="parameters">Any existing, non-OAuth query parameters desired in the request</param>
205         public virtual OAuthWebQueryInfo BuildClientAuthAccessTokenInfo(WebMethod method, WebParameterCollection parameters)
206         {
207             ValidateClientAuthAccessRequestState();
208
209             if (parameters == null)
210             {
211                 parameters = new WebParameterCollection();
212             }
213
214             var uri = new Uri(AccessTokenUrl);
215             var timestamp = OAuthTools.GetTimestamp();
216             var nonce = OAuthTools.GetNonce();
217
218             AddXAuthParameters(parameters, timestamp, nonce);
219
220             var signatureBase = OAuthTools.ConcatenateRequestElements(method, uri.ToString(), parameters);
221             var signature = OAuthTools.GetSignature(SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret);
222
223             var info = new OAuthWebQueryInfo
224                            {
225                                WebMethod = method,
226                                ParameterHandling = ParameterHandling,
227                                ClientMode = "client_auth",
228                                ClientUsername = ClientUsername,
229                                ClientPassword = ClientPassword,
230                                ConsumerKey = ConsumerKey,
231                                SignatureMethod = SignatureMethod.ToRequestValue(),
232                                SignatureTreatment = SignatureTreatment,
233                                Signature = signature,
234                                Timestamp = timestamp,
235                                Nonce = nonce,
236                                Version = Version,
237                                UserAgent = "Hammock",
238                                TokenSecret = TokenSecret,
239                                ConsumerSecret = ConsumerSecret
240                            };
241
242             return info;
243         }
244
245         public virtual OAuthWebQueryInfo BuildProtectedResourceInfo(WebMethod method, 
246                                                             WebParameterCollection parameters,
247                                                             string url)
248         {
249             ValidateProtectedResourceState();
250
251             if (parameters == null)
252             {
253                 parameters = new WebParameterCollection();
254             }
255
256             // Include url parameters in query pool
257             var uri = new Uri(url);
258 #if !SILVERLIGHT
259             var urlParameters = System.Compat.Web.HttpUtility.ParseQueryString(uri.Query);
260 #else
261             var urlParameters = uri.Query.ParseQueryString();
262 #endif
263
264 #if !SILVERLIGHT
265             foreach (var parameter in urlParameters.AllKeys)
266 #else
267             foreach (var parameter in urlParameters.Keys)
268 #endif
269             {
270                 switch (method)
271                 {
272                     case WebMethod.Post:
273                         parameters.Add(new HttpPostParameter(parameter, urlParameters[parameter]));
274                         break;
275                     default:
276                         parameters.Add(parameter, urlParameters[parameter]);
277                         break;
278                 }
279             }
280
281             var timestamp = OAuthTools.GetTimestamp();
282             var nonce = OAuthTools.GetNonce();
283
284             // [DC] Make a copy of the parameters so that the signature double-encode isn't used
285             var copy = new WebParameterCollection();
286             foreach(var parameter in parameters)
287             {
288                 copy.Add(new WebPair(parameter.Name, parameter.Value));
289             }
290
291             AddAuthParameters(copy, timestamp, nonce);
292
293             // [DC] Escape parameters at this point; do not escape again if recalculating
294             var signatureBase = OAuthTools.ConcatenateRequestElements(method, url, copy);
295             var signature = OAuthTools.GetSignature(
296                 SignatureMethod, SignatureTreatment, signatureBase, ConsumerSecret, TokenSecret
297                 );
298             
299             var info = new OAuthWebQueryInfo
300                            {
301                                WebMethod = method,
302                                ParameterHandling = ParameterHandling,
303                                ConsumerKey = ConsumerKey,
304                                Token = Token,
305                                SignatureMethod = SignatureMethod.ToRequestValue(),
306                                SignatureTreatment = SignatureTreatment,
307                                Signature = signature,
308                                Timestamp = timestamp,
309                                Nonce = nonce,
310                                Version = Version ?? "1.0",
311                                Callback = CallbackUrl,
312                                UserAgent = "Hammock",
313                                ConsumerSecret = ConsumerSecret,
314                                TokenSecret = TokenSecret
315                            };
316             
317             return info;
318         }
319
320         private void ValidateTokenRequestState()
321         {
322             if (RequestTokenUrl.IsNullOrBlank())
323             {
324                 throw new ArgumentException("You must specify a request token URL");
325             }
326
327             if (ConsumerKey.IsNullOrBlank())
328             {
329                 throw new ArgumentException("You must specify a consumer key");
330             }
331
332             if (ConsumerSecret.IsNullOrBlank())
333             {
334                 throw new ArgumentException("You must specify a consumer secret");
335             }
336         }
337
338         private void ValidateAccessRequestState()
339         {
340             if (AccessTokenUrl.IsNullOrBlank())
341             {
342                 throw new ArgumentException("You must specify an access token URL");
343             }
344
345             if (ConsumerKey.IsNullOrBlank())
346             {
347                 throw new ArgumentException("You must specify a consumer key");
348             }
349
350             if (ConsumerSecret.IsNullOrBlank())
351             {
352                 throw new ArgumentException("You must specify a consumer secret");
353             }
354
355             if (Token.IsNullOrBlank())
356             {
357                 throw new ArgumentException("You must specify a token");
358             }
359         }
360
361         private void ValidateClientAuthAccessRequestState()
362         {
363             if (AccessTokenUrl.IsNullOrBlank())
364             {
365                 throw new ArgumentException("You must specify an access token URL");
366             }
367
368             if (ConsumerKey.IsNullOrBlank())
369             {
370                 throw new ArgumentException("You must specify a consumer key");
371             }
372
373             if (ConsumerSecret.IsNullOrBlank())
374             {
375                 throw new ArgumentException("You must specify a consumer secret");
376             }
377
378             if (ClientUsername.IsNullOrBlank() || ClientPassword.IsNullOrBlank())
379             {
380                 throw new ArgumentException("You must specify user credentials");
381             }
382         }
383
384         private void ValidateProtectedResourceState()
385         {
386             if (ConsumerKey.IsNullOrBlank())
387             {
388                 throw new ArgumentException("You must specify a consumer key");
389             }
390
391             if (ConsumerSecret.IsNullOrBlank())
392             {
393                 throw new ArgumentException("You must specify a consumer secret");
394             }
395
396             /*
397             if (Token.IsNullOrBlank())
398             {
399                 throw new ArgumentException("You must specify a token");
400             }
401
402             if (TokenSecret.IsNullOrBlank())
403             {
404                 throw new ArgumentException("You must specify a token secret");
405             }
406             */
407         }
408
409         private void AddAuthParameters(ICollection<WebPair> parameters, string timestamp, string nonce)
410         {
411             var authParameters = new WebParameterCollection
412                                      {
413                                          new WebPair("oauth_consumer_key", ConsumerKey),
414                                          new WebPair("oauth_nonce", nonce),
415                                          new WebPair("oauth_signature_method", SignatureMethod.ToRequestValue()),
416                                          new WebPair("oauth_timestamp", timestamp),
417                                          new WebPair("oauth_version", Version ?? "1.0")
418                                      };
419
420             if (!Token.IsNullOrBlank())
421             {
422                 authParameters.Add(new WebPair("oauth_token", Token));
423             }
424
425             if (!CallbackUrl.IsNullOrBlank())
426             {
427                 authParameters.Add(new WebPair("oauth_callback", CallbackUrl));
428             }
429
430             if (!Verifier.IsNullOrBlank())
431             {
432                 authParameters.Add(new WebPair("oauth_verifier", Verifier));
433             }
434
435             if(!SessionHandle.IsNullOrBlank())
436             {
437                 authParameters.Add(new WebPair("oauth_session_handle", SessionHandle));
438             }
439
440             foreach (var authParameter in authParameters)
441             {
442                 parameters.Add(authParameter);
443             }
444         }
445
446         private void AddXAuthParameters(ICollection<WebPair> parameters, string timestamp, string nonce)
447         {
448             var authParameters = new WebParameterCollection
449                                      {
450                                          new WebPair("x_auth_username", ClientUsername),
451                                          new WebPair("x_auth_password", ClientPassword),
452                                          new WebPair("x_auth_mode", "client_auth"),
453                                          new WebPair("oauth_consumer_key", ConsumerKey),
454                                          new WebPair("oauth_signature_method", SignatureMethod.ToRequestValue()),
455                                          new WebPair("oauth_timestamp", timestamp),
456                                          new WebPair("oauth_nonce", nonce),
457                                          new WebPair("oauth_version", Version ?? "1.0")
458                                      };
459
460             foreach (var authParameter in authParameters)
461             {
462                 parameters.Add(authParameter);
463             }
464         }
465     }
466 }