Introduction
This can be considered as an extension to the article Secure WCF RESTful service using OAUTH. In the earlier article, you can find the examples on how to create a restful WCF service using OAUTH and how to create a .NET based consuming application.
I have seen the comments section of the article and most of the people were requesting for how to consume the restful service using JavaScript code. So this has encouraged me to write my first ever article on CodeProject. I hope this would be helpful for people out there and further encourages me to write more.
I have modified the example so that a realistic scenario is considered. Before diving deep into the article, please download the required source code required for OAUTH.
Using the Code
Following are the methods which are exposed by our restful WCF service, alongside the description.
Method
| Description
|
GetBooksList
| Gets the list books that are stored in the DB in the form of serialized List of Book .NET object as XML string. This method doesn’t require any OAUTH authentication for accessing.
|
GetBookById
| Gets the book by ID. Queries the DB for the available book with the given ID and returns Book .NET object as XML string. This method doesn’t require any OAUTH authentication for accessing.
|
AddBook
| Adds the book to the DB. This method doesn’t require any OAUTH authentication for accessing.
|
UpdateBook
| Updates the book to the DB based on the ID passed. This method doesn’t require any OAUTH authentication for accessing.
|
DeleteBook
| Deletes the book to the DB based on the ID passed. This method doesn’t require any OAUTH authentication for accessing.
|
GetBookNames
| Gets the book names that are available in the DB as JSON object. This method doesn’t require any OAUTH authentication for accessing.
|
GetBookNames_WithOAuth
| Gets the book names that are available in the DB as JSON object. This method requires OAUTH authentication for accessing
|
So we would require the last method in the list, which is our point of interest for this article. I would expose few lines of code which would be almost similar to our parent article.
public List<string> GetBookNames_WithOAuth()
{
try
{
if (Authenticate(WebOperationContext.Current.IncomingRequest))
{
using (LibraryEntities entities = new LibraryEntities())
{
return entities.Books.Select(b => b.BookName).ToList();
}
}
else
{
WebOperationContext.Current.OutgoingResponse.StatusCode = HttpStatusCode.Unauthorized;
return new List<string> { "Unauthorized Request." };
}
}
catch (Exception ex)
{
throw ex;
}
}
private bool Authenticate(IncomingWebRequestContext context)
{
bool isAuthenticated = 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 = "secretkey";
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
);
isAuthenticated = pa["oauth_signature"] == hash;
}
return isAuthenticated;
}
There is nothing much to explain about this code. The web method would authenticate the request before returning the list of book names available in the DB. Authenticate method would get the reference from OAUTH class.
Now, we will create a client Web Application which will consume the above restful WCF service using JavaScript and OAUTH authentication.
Create a normal web application from Visual Studio with framework as .NET 4.0. Include oauth.js and sha1.js files downloaded from the GoogleCode links provided in the introduction section. Create a custom JavaScript file with name as ConsumeRest.js file. Below is the screen shot for how the solution explorer would look like.
Below is the sample code which needs to include in ConsumeRest.js file.
function consumeRestWCFService() {
var accessor =
{
consumerSecret: "secretkey",
tokenSecret: null,
serviceProvider:
{
accessTokenURL: "http://localhost:7776/libraryservice/OAuthBooks"
},
consumerKey: "test"
};
var message =
{
action: accessor.serviceProvider.accessTokenURL,
method: "GET",
parameters: []
};
message.parameters.push(["oauth_consumer_key", accessor.consumerKey]);
message.parameters.push(["oauth_signature_method", "HMAC-SHA1"]);
message.parameters.push(["oauth_version", "1.0"]);
message.parameters.push(["oauth_timestamp", ""]);
message.parameters.push(["oauth_nonce", ""]);
message.parameters.push(["oauth_signature", ""]);
OAuth.setTimestampAndNonce(message);
OAuth.SignatureMethod.sign(message, accessor);
var authorizationHeader = OAuth.getAuthorizationHeader("", message.parameters);
var requestBody = OAuth.formEncode(message.parameters);
var requestToken = newXMLHttpRequest();
requestToken.onreadystatechange = function()
{
if (requestToken.readyState == 4) {
alert(requestToken.responseText);
}
};
requestToken.open(message.method, message.action + "?" + requestBody, true);
requestToken.setRequestHeader("Authorization", authorizationHeader);
requestToken.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
requestToken.send(null);
}
function newXMLHttpRequest() {
try {
return new XMLHttpRequest();
} catch (e) {
try {
return new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
try {
return new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
alert("Sorry, your browser doesn't support AJAX.");
throw e;
}
}
}
}
In the above JavaScript code, we are setting the values for accessor and message variables. Accessor variable would have all the defined values which are required for communicating with the restful WCF service. The consumerSecret
attribute must be the same as that used in the WCF application, otherwise unauthorized access would be prompted. Also please change the accessTokenURL
attribute, which is nothing but the WCF service URL.
We are pushing the required parameters to the message variable which is required for OAuth. OAuth.setTimestampAndNonce
would set oauth_timestamp
and oauth_nonce
attributes of message variable. All these attributes would be required to generate a signature key and some encryption logic is used to generate this key. In this example, we are using SHA1 encryption to generate the signature key. OAuth.SignatureMethod.sign
method would generate the signature key and set the oauth_signature
attribute of message variable.
For sending the request asynchronously, we are using XMLHttpRequest
and ActiveXObject
. The remaining code is self-explanatory.
Create or use the existing Web form to call the method consumeRestWCFService()
. I have written my code on default.aspx page by adding an anchor link with below as sample code. Also please add references to the JavaScript files that are used.
<script src="Scripts/sha1.js"></script>
<script src="Scripts/oauth.js"></script>
<script src="Scripts/ConsumeRest.js"></script>
<a href="javascript:consumeRestWCFService();">Call REST WCF</a>
If you look at the restful WCF service URL, it seems to be unusual as the .svc is missing. I have used the routing concepts of .NET 4.0 to route/rewrite the required URL. Below is the sample code of the same.
Needs to be written on the Interface:
[OperationContract]
[WebGet(UriTemplate = "OAuthBooks", ResponseFormat = WebMessageFormat.Json)]
List<string> GetBookNames_WithOAuth();
Needs to be written on the Global.asax:
protected void Application_Start(object sender, EventArgs e)
{
RouteTable.Routes.Add(new ServiceRoute
("libraryservice", new WebServiceHostFactory(), typeof(BookService)));
}
Finally, when you execute your client application, you should be alerted with list of books available in the DB in JSON format. If you are facing any difficulty, please download the source code and help it yourselves. This example purely deals with JavaScript, this can be further extended to use jQuery. Hope this would be helpful for most people like me.
Happy coding!!! Cheers!!!