2011年6月2日木曜日

Unique Authentication using Magic of HTML5 and WCF

This is my first hands on in HTML5. One day I thought about making an application which is simple and fancy looking as well. I had not worked earlier in HTML5, but once I saw the new features and tags in HTML5, I decided to make something new in core HTML. The biggest advantage in using HTML5 is that it is light weighted, platform independent and best for mobile applications like Iphone, Android (For Win 7 mobile, it's not supported because Microsoft is still experimenting with HTML5 and has not yet introduced HTML5 in mobile). Enough about my thoughts, let's moves on to the application. This application is based on Login form, but it is not a simple authentication form.
In this article, we'll build a innovative login form, where the user drops a "webid" file in the login form to authenticate. The file contains an image and some information about the card holder. It uses a great feature of HTML5, WCF and feature called WebId.
System Requirements
The most essential requirement to run this application is HTML5 supported browser.
Browser: Firefox 3.6.3, Google Chrome 5.0, Apple Safari 4.05, Opera 10.53
What You Can Learn
As a beginner, you can learn the following features:
a. HTML5, CSS3, WebKit
b. Ajax call to WCF service using JavaScript
c. JSON Operation in JavaScript.
Fancy Signup Form

I used this term (fancy) because I will tell you in simple terms how you can make a form look attractive.
As in figure of signup form, I divide the form in 3 sections:
1. User detail
2. Address detail
3. Image Upload
All sections are pretty simple except image upload section where I did not give any upload button. So how I can upload image? So I used the feature of drag and drop of HTML5 where you have to just drag and drop image in box. But here I applied a constraint along with it. You can't drag and drop image of more than 10 KB size. Since this is simple demo application, I was not going deep into it to resize image of any size, so I just put the constraint.
As you can see in the signup form rounded corner box, this is very simple CSS for webkit in HTML5:
-moz-border-radius: 5px;
-webkit-border-radius: 5px;
-khtml-border-radius: 5px;
border-radius: 5px;
You can give gradient effect using webkit at background in one line.
background: -webkit-gradient(linear, 0% 0%, 0% 100%, from(#526ACE), to(#526ACE));
And you can browse more details in CSS from my code in the attachment.
Image Upload
Especially I wanted to give more details about this feature because it is more interesting for lazy people who don't want to navigate an image from desktop location and then upload it. I saved 5-6 seconds by using drag and drop feature of HTML5.
Let's have a look at how I can accomplish this task.
<fieldset>
<legend>Image Load</legend>
<ol>
<li><legend>Drag and Drop your Image here<legend>
<div id="holder">
</div>
<div id="status">
</div>

<script>
var y = null;
var holder = document.getElementById('holder'),
state = document.getElementById('status');

if (typeof window.FileReader === 'undefined') {
state.className = 'fail';
} else {
}
holder.ondragover = function()
{ this.className = 'hover'; return false; };
holder.ondragend = function()
{ this.className = ''; return false; };
holder.ondrop = function(e) {
this.className = '';
e.preventDefault();
var size = e.dataTransfer.files[0].size;
if (size > 10000) {
alert("Your image size is greater than
10 kb please Shrink image size");
window.location.reload(true);
}
else {
var file = e.dataTransfer.files[0],
reader = new FileReader();

reader.onload = function(event) {
holder.style.background =
'url(' + event.target.result + ') no-repeat center';
y = 'url(' + event.target.result + ') no-repeat center';
};
reader.readAsDataURL(file);
//var x = document.getElementById(el);
state.className = 'success';
state.innerHTML = 'Image SuccessFully Uploaded';
return false;
}
};
</script></li>
</ol>
</fieldset>
Although this code is self explanatory, I want to give some details on drag and drop feature. As you can see, I used div as a place in the form for drag and drop.
In the JavaScript, we are:
1. Searching for the drop target in the DOM using document.getElementByID.
2. When drag over event is fired (when the user drags the element over another), it will trigger the CSS class.
3. Bind the drop event, and within there, grab some data about what was dropped.
4. Now read the stream by reader and it will generate event.target.result that will help us to get an image in base 64 format which we further used as in webid.
For more details on new tags, please move to resources.
That is enough for fancy form decoration and JavaScript. Now we will move to Ajax calling WCF service.

Ajax Calls to WCF Service
Before describing snippet of Ajax call, I would like to give you a few details on WCF service at the server end. The service exposed as a REST URI in Post method. I want to send data as post because data would be too large. I used response format in JSON because it is easy to use in Ajax call. As this is a demo application, I did not use WCF in a professional way and just use as rooky.
Simple one method Signup User:
[OperationContract]
[WebInvoke(Method = "*",
ResponseFormat = WebMessageFormat.Json,
BodyStyle = WebMessageBodyStyle.Wrapped,
UriTemplate = "SignUpUser")]
string SignUpUser(string name, string email,string language, string phoneno,
string gender, string country, string image);
Implementation of the SignUpUser is very straight forward. I just make a webid format just as a JSON Format and send it to the appropriate mail. For making JSON string, I used inbuilt .NET serializer.
JavaScriptSerializer oSerializer = new JavaScriptSerializer();
string sJSON = oSerializer.Serialize(jsonWebIdList);
Web ID Format
We should create a file that we can drop on the form. It will be a text file with the extension "webid". The content is a JSON object containing all the data we need. One part of the file, named userdata, lists things as name, age, etc. Remember that you shouldn't trust the data in the file. It should only be used as feedback to the user on the login screen.
{
"filetype": "webid",
"signed":1234567890,

"userdata": {
"id": 1,
"name":"XYZ",
"gender": "Male",
"birthdate":19610804,
"phone":"1234567890",
"country":"us",
"language":"en_US",
"image": "" // Base 64 Image format
},
"keys": {
"Null"
}
}
Now I call signup service from JavaScript that will look like this:
var baseUrl = "http://localhost:54976/RestServiceImpl.svc/";

//Ajax request function for making ajax calling through other object
function AjaxRequest(baseurl, type, callbackResponse, parameterString) {
this.BaseURL = baseurl;
this.Type = type;
this.Callback = callbackResponse;
this.createXmlRequestObject();
this.ParemeterString = parameterString;
}

// Create XMLHTTP OBJECT
AjaxRequest.prototype.createXmlRequestObject = function() {
if (window.ActiveXObject) { // INTERNET EXPLORER
try {
this.xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch (e) {
this.xmlHttp = false;
}
}
else { // OTHER BROWSERS
try {
this.xmlHttp = new XMLHttpRequest()
} catch (f) {
this.xmlHttp = false;
}
}

if (!this.xmlHttp) { // RETURN THE OBJECT OR DISPLAY ERROR
alert('there was an error creating the xmlhttp object');
} else {
//return this.xmlhttp;
}
}

AjaxRequest.prototype.MakeRequest = function() {
try {

// PROCEED ONLY IF OBJECT IS NOT BUSY
if (this.xmlHttp.readyState === 4 || this.xmlHttp.readyState === 0) {

// EXECUTE THE PAGE ON THE SERVER AND PASS QUERYSTRING
this.xmlHttp.open(this.Type, this.BaseURL, false);

var that = this;
// DEFINE METHOD TO HANDLE THE RESPONSE
this.xmlHttp.onreadystatechange = function() {
try {

// MOVE FORWARD IF TRANSACTION COMPLETE
alert(that.xmlHttp.readyState);
if (that.xmlHttp.readyState == 4) {
alert(that.xmlHttp.status);
// STATUS OF 200 INDICATES COMPLETED CORRECTLY
if (that.xmlHttp.status == 200) {

// WILL HOLD THE XML DOCUMENT
var xmldoc;
if (window.ActiveXObject) { // INTERNET EXPLORER
xmldoc = new ActiveXObject("Microsoft.XMLDOM");
xmldoc.async = "false";
that.Callback(that.xmlHttp.responseText);
}
else { // OTHER BROWSERS
//writeMessage("MakeRequest", that.xmlHttp.responseText);
that.Callback(that.xmlHttp.responseText);
}
}
}
}
catch (e)
{ alert(e) }
}

switch (this.Type) {
case "GET":
//this.xmlHttp.setRequestHeader("Content-type", "application/json");
// MAKE CALL
this.xmlHttp.send(this.BaseURL);
break;
case "POST":
this.xmlHttp.setRequestHeader("Content-type", "application/json");
this.xmlHttp.send(this.ParemeterString)
}

}
else {
// IF CONNECTION IS BUSY, WAIT AND RETRY
setTimeout('GetAllAppsService', 5000);
}
} catch (e) {
alert(e);
}
}
As you can see from the above code function, AjaxRequest creates XMLHttpRequest() object which further calls method AjaxRequest.prototype.MakeRequest. I used JavaScript in object oriented so that it can be used anywhere in calls easily. What you need to do is just make an object of AjaxRequest and call the function MakeRequest. For more details on how to use JavaScript as OOPS, please follow the trick from here.
You can also get some help form this article here.
Now call the Ajax request to WCF service just like:
AuthenticateLogin.prototype.SendDetailsToServer = function(parameters, localId) {

var url = baseUrl + "SignUpUser";
var parameterString = "{";

for (var i = 0; i < parameters.length; i++) {
parameterString = parameterString + '"'
+ parameters[i][0] + '":"'
+ parameters[i][1] + '" ,';
}

parameterString = parameterString.slice(0, parameterString.length - 1);
//writeMessage("AddNewReminderToServer", "Local id : "+localId);
parameterString = parameterString + "}";
var ajaxRequestObject = new AjaxRequest(url, "POST", function(responseText) {
var jsonobj = eval('(' + responseText + ')');
var result = jsonobj.SignUpUserResult;
if (result == "Successful") {
alert("SuccessfullyMail sent and you will redirect to login Page");
window.location = "http://localhost:54976/UI/latestLogin.htm";
}
else {
alert("Message sending Fail! Please try again");
window.location.reload(true);
}
// writeMessage("handleresponse", jsonstr);
// writeMessage(" -> local id :", ajaxRequestObject.TempItemID);
}, parameterString);

ajaxRequestObject.TempItemID = localId;
//writeMessage("AddNewReminderToServer", "Local id in ajax object : " +
//ajaxRequestObject.TempItemID);
ajaxRequestObject.MakeRequest();

}
One thing I would like to stay focused on is parameterString. I customized Body request in JSON Format because Ajax request header is in JSON format. So it will only accept JSON string in body.
this.xmlHttp.setRequestHeader("Content-type", "application/json");
this.xmlHttp.send(this.ParemeterString)
Here, function (responseText) is used as a callback function which will call once response is handed over by the Ajax request call. ResponseText is the result state when response sends back from the server in readystate 4 with status 200.
Now call:
function getDataFromthroughClass() {
var objSync = new AuthenticateLogin();
//string name, string email, string phoneNo, string gender, string country)
var name = document.getElementById("name").value;
var email = document.getElementById("email").value;
var phone = document.getElementById("phone").value;
var language = document.getElementById("language").value;
var gender = document.getElementById("gender").value;
var country = document.getElementById("country").value;
objSync.SendDetailsToServer(new Array(
new Array("name", name),
new Array("email", email),
new Array("language", language),
new Array("phoneno", phone),
new Array("gender", gender),
new Array("country", country),
new Array("image", y)));
}
Login Form
This section is pretty interesting and something different which I got from mattiasdanielsson. He gives a nice way to use web id as an authentication by using the drag and drop feature of HTML5.
What we used in login form when the user wants to authenticate, he drops a file (i.e. "xyz.webid") in the form, which is then read and parsed by JavaScript as JSON. Using jQuery, the users data (name, gender, etc.) is displayed in the drop zone, providing visual feedback to the user. If the file is parsed without error, an input is shown where the user enters his four-digit PIN number. The JavaScript then uses the PIN together with the "auth" string in the dropped file to create the key sent to the server... In this demo application, I don't use PIN authentication from server.
var objData;

$(document).ready(function() {

var $droptarget = $('#idBox'), $idCardSrc = $('#idCardSrc'),
$idBoxBg = $('#idBoxBg'),
$pinBox = $('#pinBox'), $pinInput = $('input', $pinBox);

$droptarget.bind('dragenter', function(e) {
e.stopPropagation();
e.preventDefault();
$droptarget.addClass('drophover');
$idBoxBg.text('Drop it now');
return false;
});
$droptarget.bind('dragleave', function(e) {
e.stopPropagation();
e.preventDefault();
$droptarget.removeClass('drophover');
$idBoxBg.text('Drop ID file here');
return false;
});
$droptarget.bind('dragover', function(e) {
e.stopPropagation();
e.preventDefault();
});
document.getElementById('idBox').addEventListener('drop', function(e) {
e.stopPropagation();
e.preventDefault();
For drag and drop of webid, we are required to add event handlers to all four events, and use both stopPropagation and preventDefault on them. Otherwise, your browser will just display the dropped file, and never fire the drop event. Also note that jQuery's bind() method is used with the first three handlers, but not the fourth. Since jQuery doesn't support the Event.dataTransfer object, we have to bind the drop event using native JavaScript.
Conclusion
I hope you will enjoy this different authentication login window by the magic of HTML5. As I stated from the outset, we can enhance this login authentication in a more logical way for very secure sites by the following method:
a. You can match base 64 image for validation.
b. Pin could be hashed in webid or just drop this pin in mail along with attachment of webid.
c. Encrypt webid (In this demo application, I did not used encryption.)
That's all about the application. For more details, please use the discussion panel.

Use HTML5 LocalStorage in your Mobile Web Applications

LocalStorage or DOM Storage provides Web pages with the ability to store named key/value pairs locally. This storage is different from the browser cookies. In some systems, it allows Web applications to store from 5MB to 10MB of application data. Unlike cookies, this data is not posted back to the server. It remains locally on the device, and it is persisted even after navigating to another site or closing the browser. Like cookies, this data can be removed if the user clears the browsing history.

This storage is ideal for Web mobile applications that do not require a lot of data storage, and all the data access can be done with scripting objects. When using the local storage, you should consider the following:

1. Validate that the local storage is available. This is available on browsers that support HTML5 and IE8+. Devices like IPhone, Android and BlackBerries support HTML5. Windows Mobile 7 may be getting HTML5 support in the middle of 2011.
2. For the keys, use a namespace that differentiates the Web applications in the same domain and append a unique token for that record.
3. Always check that the storage limit has not been reached. This exception would be raised when adding new items.

Simple data provider to illustrate the ability to add and read values from the Local Storage:

og.Data.Storage = {
isSupported: function () {
try {
return ('localStorage' in window && window['localStorage'] !== null);
} catch (e) {
return false;
}
},
Add: function (key, value) {
try {
localStorage.setItem(key, value);
//or localStorage[key] = value; //like associative arrays
} catch (e) {
alert(e.Description);
return -1;
}
},
Get: function (key) {
return localStorage.getItem(key);
//or localStorage[key];
}
}

This simple data provider implementation can be used to test if the localStorage is available. It also supports Add and Get methods. To use the provider, you could write a simple script as follows:

if (og.Data.Storage.isSupported()){
og.Data.Storage.Add("myapp.key1","value 1"); //notice the key name
og.Data.Storage.Add("myapp.key2","value 2");
var val1= og.Data.Storage.Get("myapp.key1");
var val2= og.Data.Storage.Get("myapp.key2");
}

If you need data storage for small Web apps, this may be something to look into.