To get started with this article, we will build a WCF Restful service which is a so called service provider & client application (in this case its a web app) which uses services so it is called a consumer.
I wanted to build a security layer on top of my WCF Restful service. So when I Google I found many links targeting OAUTH & all the articles or posts talk about using OAUTH with complicated examples & I could not figure out where to start & how to proceed to achieve this.
After my many clicks on Google, I could find pieces of code to understand what needs to be done to provide security for my Restful services.
So in this article, I will try to keep things as simple as possible & showcase how to achieve this complicated task.
So what is OAUTH?
OAuth is the standardization and combined wisdom of many well-established industry protocols. It is similar to other protocols currently in use (Google AuthSub, AOL OpenAuth, Yahoo BBAuth, Upcoming API, Flickr API, Amazon Web Services API, etc).
DotNetOpenAuth is a consumer and service provider implementation for OAuth 1.0 and 1.0a for .NET, written in C#. It has built-in support for HMAC-SHA1, RSA-SHA1, and PLAINTEXT signature methods with extensibility to add others.
Now let's get on to the code part. In this article, I will not discuss how to build a RESTFUL service from scratch.
WCF Restful Service Using OAUTH
Ok, in my WCF Restful service I have implemented a get method (GetSampleMethod_Without_OAuth) without using OAUTH. The following is the simple implementation (I hope it is quite simple):
- [OperationContract(Name = "GetSampleMethod_Without_OAuth")]
- [WebGet(UriTemplate = "GetSampleMethod_Without_OAuth/inputStr/{name}")]
- string GetSampleMethod_Without_OAuth(string name);
-
- public string GetSampleMethod_Without_OAuth(string strUserName)
- {
- StringBuilder strReturnValue = new StringBuilder();
-
- strReturnValue.Append(string.Format("You have entered userName as {0}", strUserName));
- return strReturnValue.ToString();
- }
So to verify this method open the browser with the following URL:
https://localhost/MyService.svc/GetSampleMethod_Without_OAuth/inputStr/suryaprakash
In this case you will see the output & if you notice this service is wide open to anyone. So to overcome such issues we can use the following implementation.
Now let's implement another method with OAuth security. This method would return the same output as above but before processing, it would check for CONSUMERSECRET which is supposed to be sent from the client to make use of the services.
If the key doesn't match it would return an unauthorized request.
This consumer secret can be shared to multiple clients. Apart from the consumer secret there are parameters like oauth_timestamp, oauth_nonce, URL etc which must be sent (more details on https://oauth.net/code/).
In my case the consumer secret is "suryabhai" which has to be sent along with the service call. The Authenticate method would read all input parameters & send it to the OAuthBase class & finally it would return true or false which will define whether to process the request or deny the request.
Let's find the following code:
- [OperationContract(Name = "GetSampleMethod_With_OAuth")]
- [WebGet(UriTemplate = "GetSampleMethod_With_OAuth/inputStr/{name}")]
- string GetSampleMethod_With_OAuth(string name);
-
- public string GetSampleMethod_With_OAuth(string strUserName)
- {
- if (Authenticate(WebOperationContext.Current.IncomingRequest))
- {
- StringBuilder strReturnValue = new StringBuilder();
-
- strReturnValue.Append(string.Format("You have entered userName as {0}", strUserName));
- return strReturnValue.ToString();
- }
- else
- {
- WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
- return "Unauthorized Request.";
-
- }
- }
- private static bool Authenticate(IncomingWebRequestContext context)
-
- {
- bool Authenticated = false;
-
- string normalizedUrl;
- string normalizedRequestParameters;
-
-
- NameValueCollection pa = context.UriTemplateMatch.QueryParameters;
-
- if (pa != null && pa["oauth_consumer_key"] != null)
- {
-
- string uri = context.UriTemplateMatch.RequestUri.OriginalString.Replace
- (context.UriTemplateMatch.RequestUri.Query, "");
-
- string consumersecret = "suryabhai";
-
- OAuthBase oauth = new OAuthBase();
-
- string hash = oauth.GenerateSignature(
- new Uri(uri),
- pa["oauth_consumer_key"],
- consumersecret,
- null,
- null,
- "GET",
- pa["oauth_timestamp"],
- pa["oauth_nonce"],
- out normalizedUrl,
- out normalizedRequestParameters
- );
-
- Authenticated = pa["oauth_signature"] == hash;
- }
-
- return Authenticated;
- }
So far, so good; the implementation is done. Now let's open the browser & call the service:
https://localhost/MyService.svc/GetSampleMethod_With_OAuth/inputStr/suryaprakash
When you make a request to the GetSampleMethod_With_OAuth method as above, the service would return "UNAUTHORIZED REQUEST" as we did not supply a consumersecret & other parameters.
To complete this article, now let's go ahead & implement the client which will call the above method by sending all necessary inputs/parameters.
As part of the client implementation, we would just make a call to the service using a WebRequest by providing all necessary parameters & the client code must use a consumersecret which is shared by the service.
- string consumerKey = "test";
- string consumerSecret = "suryabhai";
- var uri = new Uri("http://localhost/MyService.svc/GetSampleMethod_With_OAuth/inputStr/suryaprakash");
- string url, param;
- var oAuth = new OAuthBase();
- var nonce = oAuth.GenerateNonce();
- var timeStamp = oAuth.GenerateTimeStamp();
- var signature = oAuth.GenerateSignature(uri, consumerKey,
- consumerSecret, string.Empty, string.Empty, "GET", timeStamp, nonce,
- OAuthBase.SignatureTypes.HMACSHA1, out url, out param);
-
- WebResponse webrespon = )WebRequest.Create(string.Format("{0}?{1}&oauth_signature={2}", url, param, signature)).GetResponse();
- StreamReader stream = new StreamReader(webrespon.GetResponseStream());
- txtResult.Text = stream.ReadToEnd();
Now let's call the service by opening the default.aspx page in the browser, as we are using a valid consumersecret the output would be as expected. Now let's modify the consumersecret & open the default.aspx; in this case the expected output is "UNAUTIRUZED REQUEST".
My example talks about only the GET method & sending data in a query string but the example can be extended to the POST method as well & we can send data in headers instead of a query string.
Happy Coding... Hope this helps!