Saturday, September 30, 2006

Single Sign On across multiple ASP.NET applications

Machine Key

The machineKey element of the web.config file is used to specify keys for encryption and decryption of forms authentication cookies and view state data, and also when dealing with out of process session state.
The default configuration uses auto generated decryption key and validation key, and SHA1 encryption type.
Using the default configuration, different web applications have different decryption and validation key, since they are randomly generated. To force 2 applications to use the same keys, they must be explicitly defined in the web.config file
A complete description of the machineKey element is available here

Single Sign On

Single Sign On (SSO) is a term used to indicate when a pool of applications need a centralized authentication, so that users login once and access to any application.

Implementing a single sign on is quite simple, and can be done by configuring the applications using the web.config file.

 

A default configuration for forms authentication is defined as follows:

<configuration> 
  ... 
  <system.web> 
    ... 
    <authentication mode="Forms"> 
      <forms name=".cookiename" 
             loginUrl="~/Login.aspx" 
             timeout="30" 
             path="/" /> 
    </authentication> 
    ... 
  </system.web> 
  ... 
</configuration> 

Form Authentication

Forms Authentication uses an authentication ticket stored in either a cookie or embedded in the url.

When used in cookie mode, the c
ookie contains authentication data, encrypted so that data can be read by the application who has created the cookie.

Cookies are associated to 2nd level domains (example.com), and can be accessed from any 3rd level domain (www.example.com, test.example.com, and so on).

where .cookiename, by default, is .ASPXFORMSAUTH.

In order for authentication data to be recognized across multiple applications, each application must be configured to use the same values for cookie name, protection and path attributes. But this isn't enough - in fact, they must also have the same machine key values (see the side box for more info about the machineKey element). These information are used to encrypt the forms authentication cookie, as mentioned in the "Forms Authentication" side box.

Below a sample web.config excerpt which must be added to each application we want single sign on enabled. In this sample, the validationKey and encryptionKey attributes must be replaced with unique values you have to generate for your applications pool.

<configuration> 
  ... 
  <system.web> 
    ... 
    <authentication mode="Forms"> 
      <forms name=".cookiename" 
             loginUrl="~/Login.aspx" 
             timeout="30" 
             path="/" 
      /> 
    </authentication> 
    ... 
    <machineKey 
       validationKey= 
         "F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902" 
      encryptionKey= 
         "F9D1A2D3E1D3E2F7B3D9F90FF3965ABDAC304902F8D923AC" 
      validation="SHA1" 
    /> 
    ... 
  </system.web> 
  ... 
</configuration> 

Second and third level domains

If the cooperating applications are installed under the same 2nd and 3rd level domain, but on different virtual folders, then no additional code is required.

If applications are installed on different second level domains (www.domain1.com and www.domain2.com), the SSO method described in this article won't work, since cookies cannot be read by applications under different second level domains. For example, if application A in domain1.com issues a cookie, the cookie may be read by A itself and any application hosted under www.domain1.com and any other 3rd level domain (test.domain1.com, beta.domain1.com, and so on). Application B in domain2.com isn't able to read the cookie, since it is hosted under a different second level domain.

If cooperating applications are installed on different third level domains, then we need to add some code in order to make SSO work. The code simply has to add the domain name to the authentication cookie, as outlined below

protected void Login (string strUserName, ...) 
{ 
  ... 
  System.Web.HttpCookie cookie; 

  cookie = FormsAuthentication.GetAuthCookie(strUserName, False); 
  cookie.Domain = "domain1.com"; 
  cookie.Expires = DateTime.Now.AddDays (-1); 
  Response.AppendCookie (cookie); 
  ... 
} 

Cookie Expiration

If different applications set different cookie expirations, the actual expiration value is the one set by the application which issued it. So if application A is configured to set an expiration of 1 hour and application B 2 hours, and the user signs in using application B, then the cookie expiration is set to 2 hours.

Logging out

Usually, in order to log out a user, a call to the Authentication.SignOut() method is used - this isn't enough when using SSO. In order to perform a single sign out, the quickest way is to set the cookie expiration to a past date - this ensures that the cookie won't be used by any application for authentication.

protected void Logout (string strUserName) 
{ 
  System.Web.HttpCookie cookie; 

  cookie = FormsAuthentication.GetAuthCookie(strUserName, false); 
  cookie.Domain = "domain1.com"; 
  cookie.Expires = DateTime.Now.AddDays (-1); 
  Response.AppendCookie (cookie); 
}

Integrating Web Applications

What said so far is valid if applications use the same database to store user profiles. But what if 2 applications use each one their own database?

In this case, the SSO works, but sooner or later one of the applications will throw exceptions due to missing data in its database. If a user registers in application A, once he signs in he can access to application B - but he never registered in application B, so application B doesn't have this user in its database.

This is the case when, for example, we have to integrate 2 existing applications, which already have their own authentication and registration implemented.

To solve this problem, we have 2 choices:

  • modify both application in order to use a single authentication and registration process, and having a shared user profile repository
  • choose one application as the master application, and remove the authentication and registration process from the other application (the slave application)

We'll focus on the second solution.

This method requires that the database used by slave applications is accessible by the master application. This can be achieved by either:

  • Create a single database which holds both application databases. In this case it would be good to use different prefixes for database entities to avoid naming conflicts - this could happen if both databases have a Users table. If we choose mst and slv as prefixes, we should rename the Users table to mst_Users for the master database and slv_Users for the slave database. This requires that we modify the source code and stored procedures.
  • Use 2 different databases, but the master application must be able to access to the slave's database.

Authentication should be performed in the following way:

  • The user accesses to the master application, and signs in
  • The master application verifies the user's credentials
  • The master application verifies whether the logged user is defined in the slave database - if not, accesses to the slave's database and creates the new user
  • The master application calls (if existing) a slave's stored procedure which performs post-authentication processing (such as setting a "logged in" field, inserting a new row in a history table, and so on)
  • The master application generates the SSO cookie

User profile creation on the slave database requires that:

  • the master application is able to access to the slave's database
  • the slave's database exposes a stored procedure which handles user registration (we may need to write it by ourselves)

The second requirement isn't mandatory, since it could also be achieved by using inline SQL - but I usually prefer the stored procedure solution.

Final touch

There would be a few final things to do on the slave application:

  • Removal of all login links
  • Replacement of logout links with a "Back to the Master application" link
  • Replacement of all "User's profile" links to point to the master application user's profile page

These steps ensure that navigation is consistent with integration - we're supposing that all user's info (credentials, profile, user's preferences) are handled by the master application, so we need to modify the slave application accordingly. It is responsibility of the master application to update user's profile in the slave application.

What if different cookies are used?

There may be cases where we want to keep authentication cookies separated from master and slaves applications. In this case we can't share the authentication cookie among cooperating applications.

In this case the solution is to create an authentication cookie for the slave application from within the master application.

The code below creates an authentication cookie from the slave application:

FormsAuthenticationTicket ticket; 
HttpCookie cookie; 
string cookiestr; 

ticket = new FormsAuthenticationTicket( 
                       1, 
                       userId, 
                       DateTime.Now, 
                       DateTime.Now.AddYears (120), 
                       true, 
                       "User Data", 
                       "cookie_path" 
           ); 

cookiestr = FormsAuthentication.Encrypt(ticket); 
cookie = new HttpCookie("cookie_name", cookiestr); 
cookie.Expires = ticket.Expiration; 
cookie.Path = "cookie_path"; 

Response.Cookies.Add(cookie); 

Forms Authentication Ticket

The FormsAuthenticationTicket class is used to create and read the values of a forms authentication cookie identifying an authenticated user.

Forms authentication tickets must be encrypted using the FormsAuthentication.Encrypt() method before being issued as a cookie.

More information about the  FormsAuthenticationTicket class can be found here.

The FormsAuthenticationTicket, as its name says, is a class used to generate authentication tickets (see the side box for more details). The code sample above shows that the following parameters are used to create the ticket:

  • ticket version number
  • user id
  • date and time at which the ticket was generated
  • ticket expiration
  • whether creating a persistent cookie or limited to the current browser's session
  • user specific data to be stored in the ticket (for example, this could be a user class)
  • cookie path

References

MSDN - machineKey element

MSDN - FormsAuthenticationTicket class

Thursday, July 27, 2006

Quickly add feed capabilities to your PHP application

Although feeds aren't a new invention, lately they are getting more popular. Using feeds, we have an easy and convenient way to publish content, either news or any other sources (blogs, articles, etc.)

I had to implement feeding capabilities to a PHP application I'm working on, and I've been suggested to use FeedCreator, an open source library written, by Kai Blankenhorn, which provides support for the following feeds:

  • RSS (0.91, 1.0, 2.0)
  • PIE (0.1)
  • MBOX
  • OPML
  • ATOM
  • HTML
  • JS

The most diffused are undoubtedly RSS and Atom, but if you need less known format you can.

Adding feeds to your application is as simple as writing the following code (taken from the library documentation):

include("feedcreator.class.php"); 

$rss = new UniversalFeedCreator(); 
$rss->useCached(); // use cached version if age<1 hour 
$rss->title = "PHP news"; 
$rss->description = "daily news from the PHP scripting world"; 

//optional 
$rss->descriptionTruncSize = 500; 
$rss->descriptionHtmlSyndicated = true; 

$rss->link = "http://www.dailyphp.net/news"; 
$rss->syndicationURL = "http://www.dailyphp.net/".$_SERVER["PHP_SELF"]; 

$image = new FeedImage(); 
$image->title = "dailyphp.net logo"; 
$image->url = "http://www.dailyphp.net/images/logo.gif"; 
$image->link = "http://www.dailyphp.net"; 
$image->description = "Feed provided by dailyphp.net. Click to visit."; 

//optional 
$image->descriptionTruncSize = 500; 
$image->descriptionHtmlSyndicated = true; 

$rss->image = $image; 

// get your news items from somewhere, e.g. your database: 
mysql_select_db($dbHost, $dbUser, $dbPass); 
$res = mysql_query("SELECT * FROM news ORDER BY newsdate DESC"); 
while ($data = mysql_fetch_object($res)) { 
$item = new FeedItem(); 
$item->title = $data->title; 
$item->link = $data->url; 
$item->description = $data->short; 

//optional 
item->descriptionTruncSize = 500; 
item->descriptionHtmlSyndicated = true; 

$item->date = $data->newsdate; 
$item->source = "http://www.dailyphp.net"; 
$item->author = "John Doe"; 

$rss->addItem($item); 
} 

// valid format strings are: RSS0.91, RSS1.0, RSS2.0, PIE0.1 (deprecated), 
// MBOX, OPML, ATOM, ATOM0.3, HTML, JS 
echo $rss->saveFeed("RSS1.0", "news/feed.xml");

What makes it so special is that it is really simple to use.

The libray is open source, released under the LGPL license.

For more information and to download the library, click here

Tuesday, July 11, 2006

How to prevent validation errors from being displayed in CodeCharge Studio

I have a registration form which uses 2 dependent list boxes Country and State, where State is populated basing on the Country selection.

To dynamically load the State list box I had to add a client OnChange event to the Country list box, which submits the form. But at this point the form displays all errors (mostly missing fields) and I don't want this to happen.

This is the solution I found, which uses client side JavaScript:

The name of my form is RegisteredUser

  1. I have added a hidden field to the form, with name
    Validation, Control Source Type = "Code Expression", type Boolean and default value = True
  2. I've added a client OnChange event to the Country list box, with the following code:
    document.forms["RegisteredUser"].Validation.value = "No";
    document.forms["RegisteredUser"].submit();
  3. In the HTML code, I've located the {Errors} row, and set the id and name attributes to phErrors - it should look like:
    <!-- BEGIN Error --> 
    <tr class="Error" id="phErrors" name="phErrors"> 
      <td colspan="2">{Error}</td> 
    </tr> 
    <!-- END Error --> 
  4. I've added a client OnLoad event to the form, with the following code:
    var phErrors = document.getElementById ("phErrors");
    if (phErrors != null) 
    { 
      if (document.forms["RegisteredUser"].Validation.value == "No") 
      { 
        document.forms["RegisteredUser"].Validation.value = "Yes"; 
        phErrors.style.visibility = "hidden"; 
        phErrors.style.position = "absolute"; 
      } 
      else 
      { 
        phErrors.style.visibility = "visible"; 
        phErrors.style.position = "relative"; 
      } 
    } 
    

This code reads the value of the Validation hidden field: if the value is No, the phErrors row is hidden, otherwise it is shown.

It seems to work correctly, and also it shouldn't add the row to the database, because the validation occurs at server side, but it isn't displayed at client side (although included in the HTML).

Monday, July 10, 2006

How to fight against spamming

I receive about 20 spam messages in my mailbox every day. I know this can be considered a low number, since I know of users receiving hundreds of spam, with a variety of subjects, ranging from porn sites to viagra, medicines, casino, software, etc.

SpamCop parses reported email, sending warning information to the internet service provider responsible for hosting the services used by the spammer

I wonder how can these spammers think that I'm so stupid to use my credit card to buy their products - but indeed I'm afraid that if they persist in their illegal business is because there are people who buy from them.

Anyway, what can we do to fight against them? There are several services which detect messages and prevent them from reaching our mail client, and mail client add-ons which move suspect messages into a spam folder, so that we don't have to waste time reading messages we aren't interested in. But is this a good way to fight spammers? Do they prevent spammers from sending you emails if you don't buy from them? Unfortunately I don't think so.

What can we do? Report spam.

Since Internet has no country borders, it's quite unlikely that recurring to law leads to a result, although I think this could be done - but unfortunately it doesn't happen.

The only thing I can do is to repay them with the same money: making them waste time.

How? I started my "war" by locating, for each spam email:

  • the ISP used by the spammer to send the email
  • the web hosting company which hosts the site referenced in the spam email

The next step was to write a complain email to both recipients, asking for the spammer's account to be suspended. This way, the spammer needs to change ISP and move its website to another hosting provider. This is a cost of their business, and I'm sure spammers have planned it - and I also want to be sure to keep this cost high for them.

I was used to perform these actions manually, by reading the email header, and using tools such as nslookup to find what I needed, but this is undoubtedly a time consuming process, and I did it while the number of spams was less than 10 per day.

But there is a better way: a service where you simply have to report the mail body and headers, and it will take care of reverse-engineering the information required, automatically fill in complaint reports and send them to the proper recipients. Its name is SpamCop, and their website is www.spamcop.net. Although the service requires a subscription (quite low, since it is about 1$ for each Mbyte of reported spam), it can also be used for free, in this case with a delay screen which reminds about subscribing - about 10 seconds to wait before completing the spam email analysis.

This of course doesn't guarantee that actions are taken against spammers. There are ISP which themselves fight against spammers, and once they receive reports they verify, and if required take their actions, which can range from deactivating the spammer's accounts to charge them and using legal ways (but this last one is the least used - unfortunately).

Monday, July 3, 2006

How to better appreciate your most preferred language

The universal technology

I've seen a lot of developers telling that Linux is better than Windows, C# is better than Java, Delphi is better than VC++, and so on. But if you ask them if they know the other language they are comparing with, they say no.

I was one of them, since I believed that the best IDE was Delphi, the best platform was Windows, and the best language was C++. At least, until I started working on "real projects" and not on applications made just for fun. When I had to learn Java and use it to develop server applications on Sun platforms, I started to appreciate the language and the Unix-like environments.... and I started to believe that these technologies were better than those I thought were the best. Soon I realized that every technology is the best, depending on where it is applied.

There is no universal technology or language which solves all problems

Now I don't believe anymore that there is the universal technology or language which solves all problems. Every time I start a new project, if the customer doesn't have a specific preference, I always spend some time trying to understand what are the best tools to implement the project - and the result depends from the project itself. In web development my favorite preference is the .NET technology, for several reasons - but this is not a choice that can be done every time for every project. For example, if one of the requirements is that the application must run on open source environments, .NET wouldn’t be the proper choice.

The proper tools

During my professional experience I've tried to learn as many languages as possible. If I get confident knowledge of each language, I'm able to choose the best solution for every problem, from both the developer point of view (how simple or hard is the development using a specific language) as well as from the customer/project point of view (what are the benefits if I choose a technology instead of another). When I learned Java, I appreciated how life is easier with a memory manager which automatically disposes objects no longer referenced. When I learned C#, I appreciated the integration with the .NET technology, the foreach statement, but I also noticed how exception handling was better in Java, since exceptions must be explicitly handled within each method or the method itself must be declared to throw those exceptions which aren't handled in its body. I also, and still, like the C++ philosophy: "The developer always knows what he's doing". This is the reason why C++ offers more power with pointers and access to memory. It's irrelevant that it's not always true the developer always knows what he is doing :-) The key point is that you have the power to do almost everything, although at a price (usually more attention when dealing with pointers and dynamically allocated memory, and more debugging....)

PHP Revolution

Lately I've appreciated a language I've used in the past, and which I didn't like: PHP. The reason why I don't like PHP is that I prefer strongly typed variables, variable declarations, as well as to know from the compiler if I wrote something wrong in my code, such as the name of a variable (a common mistake in PHP which leads to unpredictable results) or a function call (which usually leads to a run-time error). But I have to say that it's extremely practical the ability to save your PHP code and test it immediately without the need of recompilation, deployment, etc. - a perfect solution for the project I'm currently working on. And I wouldn't had chosen it if the customer hadn't explicitly asked for it. We never stop learning!! Now I'll definitively choose it again in the future, if the prerequisites for its usage are good, and the project will benefit from its usage.

Can I say that C# is better than Java?

Yes and no. It depends. As outlined above, in order to state that a language or technology is better than another, I need to:

  1. have proficient knowledge of both
  2. compare pro and cons applied to a specific project

But the result of the comparison is limited to the project - on a different project, the choice could be different:

  • if I need to care about speed, maybe the best solution would be C++, but not for web applications
  • if I need portability, Java is best - but I wouldn't choose it if the application has a user interface
  • if I need modularity and easy of development on a web application, my choice would fall on ASP.NET
  • if I need low licenses cost, the choice could be PHP, Java/JSP, or Ruby On Rails

Your opinion

How do you choose the technologies and languages on your projects? Do you focus on a single technology, and use others only when the customer explicitly asks for them?

Saturday, July 1, 2006

Learning by using

The first computer

I started dealing with development when I was 12, after I convinced my parents to buy me a computer. My choice fell on a Commodore Vic 20, a computer with a 1MHz CPU, 3582 bytes (bytes!!) of RAM memory, embedded basic language, and external memory storage based on a tape recorder.

I learned the computer principles, the way it works, the Basic language, the assembler language, how to deal with limited resources (such as memory) and how to develop software. I wrote a few games, just for fun - too young to think about how to make money with them :). Since then, I have ever learned, learned and learned, and almost all of what I know I learned by myself.

The first job

When I've been contacted by a company for my first "serious" job, I've been subject to a 2 weeks test to implement some new features on an existing FoxPro application before getting a contract. Never worked with FoxPro before, nor had experience with its scripting language and more generally with database development (just a few theorical concepts).

So, during these 2 weeks, I studied and learned FoxPro, its language, and how to use databases. Life isn't so easy, so 2 days before the end of my test period I had an HD failure, and of course I didn't have any backup - all work was lost. Since it was a new HD, we brought it to the support center, asking to repair it but preserving the data, if possible - after 1 hour, a technician get back to us telling "The hard disk is working so well that we've also formatted it". Panic.

Fortunately I've been able to recover all lost data by using an unformatting utility - with compliments of my boss, who told me "I don't know why, but I thought you were a database expert - I've just read your resume again, and I have no idea why I thought so. Anyway I like the way you work, so the job is yours".

My next task was a feasibility study about a QuickTime application running on a Mac to be used to display and record data retrieved from medical equipments - once again, I had no previous experience with QuickTime and Mac - nevertheless, this has been not only the first but also the last experience with a Mac :-). In 1 week, the time given to complete this task, I provided the results of the feasibility study - which were positive.

Learning by Using

What do I want to say?

Sometimes we have to learn a new language or technology, but we have no time to learn it since we also have to apply it in the project we have to work on, and which, surprisingly, must be completed within yesterday.

I'm used to learn everything I need in my work by myself. This is undoubtedly a great advantage, since I don't need to attend courses or use other forms of slow "formal learning".

What I mostly like is the ability to learn a new language or technology by using it on a real project. Of course this can't be done without having a proficient knowledge of the area being explored. If somebody has never developed or learned a programming language, it's extremely unlikely he will be able to learn using this learning pattern.

But if, for instance, you have done web development before, using ASP.NET, it wouldn't be difficult to move on PHP. Once you've acquired what web development means, what you expect to do and what tools you have to use and you are allowed to use, you're able to transpose this knowledge on the new language - it's just a matter of translating what you already know in a new context. You expect that, independently from the language you use, there is a way to handle sessions and cookies, to access to a database, to handle data submitted in a form, etc.

This is the reason why I like to learn new languages and technologies. I like the challenge of starting from a virtually unknown area, performing analysis and design on a real project basing on a few information I read here and there, and starting the development phase while starting the learning process at the same time. This process undoubtedly slows the development at beginning, to proceed at increased speed from day to day. And while proceeding with the learning process you also realize what you've made wrong - this sometimes requires re-working on the work made in the past days, but this way you better understand what you did wrong and you (hopefully) won't do it again.

Formal learning

Actual knowledge is not as important as the ability to acquire that knowledge

Of course the "learning by using" pattern cannot be considered better than formal learning paths. Attending a course has its advantages, since who is teaching has proficient knowledge (or at least we hope he has :-)) and he can better show the key points and focus on the most important things to know, as well as explain best practices, things to know, things to avoid, and so on.

Also, reading a good book and doing practice, following code, examples and exercises, helps to get knowledge. Anyway I usually skip all of these extra activities, since I prefer to focus on understanding what the writer want to expose, catching the key points, just to know that "they exist" - when I need to use a specific feature, I know it is there, and I just have to browse the documentation to know how to use it.

Knowledge or Pointers?

Ability to find pointers to knowledge is more important than actual knowledge

What I've just written above points me to the following consideration.

I strongly believe that actual knowledge is not as important as the ability to acquire that knowledge. Long time ago I've been told "it's not important if you have a specific knowledge - I care more if you are able to find the pointers to that knowledge".

Conclusions

Traditional learning (either school, courses, and so on) is important. But it isn't enough. Being able to self-learn helps when you have no time to use the traditional learning paths. Being able to learn while applying what you're learning is an extra gear. Being able to update your knowledge "on demand" is a double extra gear.

What are your learning methodologies? Do you want to add any comment, suggestion, or extend this discussion? Just write below...

Copyright © 2013. All Rights Reserved.