Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Apply Single-Sign-On to your application

0.00/5 (No votes)
18 Sep 2003 1  
Explains the pros and cons of implementing Single-Sign-On to your application and the code implementation to do it via VB.NET and LDAP.

Brief

In this ever-evolving world of technology, applications are getting more complex by the day but the economies of scale is not there when business users want them up to market faster and place great emphasis on ease of use.

The concept of distributed computing has been around for almost a decade. However, distributed computing has mainly been confined within the walls of the organization and most importantly, vendor-specific platforms.

In the recent years, professional bodies have come together and have worked together with all the major platform vendors out there and came up with widely-accepted standards. Suddenly, platforms are not a major hindrance to communication anymore. The current explosion of bandwidth supply, coupled with the lower cost of utilizing it, plus other reasons, have also caused blurring of lines and boundaries between Local Area Network (LAN) and the Wide-Area Network (WAN). We are now seeing an evolution of technology where a single application can span across multiple platforms and transcend physical barriers, parameters and boundaries.

While I don't want to go into details of what was described above, the topic of my article will touch one aspect of application development which I believe makes some sense to be farmed out to distributed computing and that is Identity Management.

Businesses of today are getting more difficult and complex and span across to other business domains as well. Software vendors are finding it difficult to meet the requirements as the ways to do business are changing all the time as well. A full enterprise-resource planning suite is hard to build without third party involvement in terms of tools and support. After that, enterprises will demand that what is being deployed must work seamlessly with the other incumbent or new applications. One problem of that is the entry point of an application --- the Sign-on / Log-on interface. If an enterprise should deploy a portal for the users to choose and use whichever application the need desires, the question is who holds the user identity. If application A holds the main key to the user database, how would application B and C communicate with it seamlessly. Of course, with the use of widely-adopted web services and XML, the next question would be are Application A, B and C built in a way where identity management is loosely-coupled in their application architecture? If so, who should hold the key, what are to be kept in this database and very importantly, who manages it?

Ideal

One of the ways I am recommending here is the use of the organizational directory. One of the key differences between a directory and a database is the frequency of the usage and of the data and also the rate of change. A Directory is very much like the Yellow Pages. You don't refer to it every minute or second of the day (frequency of usage) and it gets published only once a year (rate of change). All basic essential user identity is being stored in a company's directory within the IT Infrastructure. This can be in IBM or Unix mainframes, Novell servers or Windows Active Directory. What is needed is a way to query and lookup the indexes of these directories to find the user and authenticate them. This is achieved by a common, widely-adopted and widely-accepted standard called Lightweight Directory Access Protocol (LDAP). Just like how Structured Query Language (SQL) queries databases, LDAP is used to query directories to return certain information.

In more ways than you think, it doesn't make sense for an application to hold another set of user data where the host platform of the application already holds a copy. The LDAP Directory is managed, rightfully, by the System Owner and he/she will also rightfully assign the proper rights to the different roles each application requires. Each user is authenticated just once with their user-credentials at logon time. From then on, their user-session is mapped onto their user-credentials, policies and authorization.

Launching an application will involve retrieving their current user session details and then querying the LDAP Directory for their user-roles and rights with that information. This is all transparent to the user and once the user gets authenticated and authorized by the LDAP Query, he/she will have access to all or portions of the application, depending on the roles of the user. Once that happens, Single-Sign-On is achieved.

Critics

Of course, the concept of Single-Sign-On in Identity Management has its critics. The most important flaw is that once a user signs-on at point of entry, he/she is signed-on as long as he/she is in the company's domain. Theoretically speaking, if he/she doesn't logged-off his/her machine, the session will always be authenticated regardless of who uses the same session later. In other words, while the user identity is not compromised, the current user session can be hijacked. This can easily happen when the user leaves his/her machine unattended and another person takes over the computer and the session. The main criteria of Single-Sign-On is that the user will only need to sign-on once (as the name suggests) and therefore, there should not be a logout button. Having one serves no purpose as it is the session that is authenticated, not the user anymore. The user only serves to create a valid session. If there is a login after a logout, then the rule of Single-Sign-On is flouted.

Another flaw of it includes the authentication of the user when the user is coming in from across domain. This will be likely if he or she uses the same application via a Internet Interface with the same application, sitting on the office domains. The user can be in their own personal domain from home or elsewhere or using another person's machine, then their own personal user-credentials will be different from the one that is stored in the organization's domain. This will require users to do multiple logins which goes against the concepts of Single-Sign-On.

Some of the flaws can be solved by user-education. Educate the users to protect their machines whenever they are not around by locking up their sessions with a password-protected screen-saver or by other similar means. As of the second flaw, until a secure and accepted tunneling protocol follows the user wherever he or she goes, that ideal may still be a way away.

Benefits

However, the benefits are heavy. User-rights, coupled with user and company policies are all centralized in a single place within the organization directory. Users have only one identity (UserIDs and Passwords) to maintain. Business owners will have the peace of mind that all entry points to applications and data are kept within a secure LDAP Directory with the company's user policy, password-policy in mind. This should be dictated by the business owners not by the software vendors. Different applications will all query one single user directory for their own authentication and should they require more application-specific data and fields, redirect the user to collect more specific information from them. The storing of other information will ultimately be mapped onto the user common name in the LDAP Directory.

Solution(s)

Therefore, the best for now would be a hybrid of both scenarios, taking the pros and cons of both. Have an application architecture with a single user repository at the Organization's LDAP Directory but letting users sign on again for added security with the same user-credentials. This may solve the problem at hand. Still the ideal is to be and can be achieved. Imagine your client email application asking you for your password each time its goes out to receive or send your emails.

Microsoft .NET has made it easy to do both with just some switches of commands at a configuration file. Therefore, it doesn't take much to deploy either solutions depending on client needs.

I will dive into some VB.NET coding to explain how both can be achieved.

Scenario A : True-Blue Single-Sign-On

LDAP SSO Flow

  1. At launch of application, query LDAP and see if the user is in the proper user-groups to access the application. I am, of course, assuming that groups are assigned to application, not individuals.
    Private Sub Page_Load(ByVal sender As System.Object, _ 
         ByVal e As System.EventArgs) Handles MyBase.Load 
      Dim p As WindowsPrincipal = Thread.CurrentPrincipal
      Dim a As New LDAPQuery
      Dim objCook As New HttpCookie("LDAPCookie")
      'Check for authentication and write into Cookie
    
      Dim sLDAPCN As String = (a.GetCNByLDAP(p.Identity.Name))
      'Check for LDAP Authentication First
    
      If sLDAPCN <> "" And _
        (a.blnFindCNinGroups(a.GetCNByLDAP(p.Identity.Name), _ 
        "AppAUserGroups")) Then
          objCook.Value = (sLDAPCN)
          Response.Cookies.Add(objCook)
          'Check for LDAP User Existence in Application Database
    
          'If User exists in Application DB, Start Application
    
          If (FindLDAPCNinDB(sLDAPCN)) Then
              'DROP Authentication Cookie token
    
              'Compute a hashed value of the current session ID
    
              'into a cookie to signal authentication success
    
              'Be sure to check for this cookie for every page load
    
              'if value doesnt match or cookie doesnt exists,
    
              'Re-do the whole authentication process again.
    
              'Single-sign-on should continue, the cookies drop
    
              'prevent authenticated LDAP Users from not having
    
              'their CN mapped with other application-specific
    
              'fields or data.
    
              Response.Redirect("Welcome.aspx")
          Else 'If False, do Database mapping
    
              Response.Redirect("CollectOtherInfo.aspx")
          End If
      Else
          Response.Redirect("LoginError.htm")
      End If
    End Sub
    Private Function FindLDAPCNinDB(ByVal sLDAPCN As String) As Boolean
    'For ease of setup, I am using a Flat XML File as a database
    
    'However, a proper way would be to use a relational database
    
    'You may use the ADO.NET to query and get information from the databse
    
      Dim xmldoc As New XmlDocument
      Dim xmlnode As XmlNode
      Dim xmlnodelist As XmlNodeList
      xmldoc.Load("D:\WebDeploy\LDAPQuery\FlatDB.xml")
      xmlnodelist = xmldoc.SelectNodes("/Users/User")
      For Each xmlnode In xmlnodelist
        'Find LDAPCN
    
        If xmlnode.ChildNodes.Item(0).InnerText = sLDAPCN Then
          Return True
        End If
      Next
      Return False
    End Function
    '###########
    
    '###########
    
    Imports System.DirectoryServices
    Public Class LDAPQuery
    'This function returns the Common Name (CN) 
    
    'of the Login information of the
    
    'LDAP Directory. The CN is an unique identity 
    
    'and cannot be changed or edited in LDAP
    
    Public Function GetCNByLDAP(ByVal strLogin As String) As String
      Dim str As String = ""
      'Parse the string to check if domain name is present.
    
      Dim idx As Integer = strLogin.IndexOf("\")
      If (idx = -1) Then
        idx = strLogin.IndexOf("@")
      End If
      Dim strDomain As String
      Dim strName As String
      If (idx <> -1) Then
        strDomain = strLogin.Substring(0, idx)
        strName = strLogin.Substring(idx + 1)
      Else
        strDomain = Environment.MachineName
        strName = strLogin
      End If
      Dim obDirEntry As DirectoryEntry = Nothing
      Try
        Dim strPath As String = "LDAP://DC=Softwaremaker,DC=net"
        obDirEntry = New DirectoryEntry(strPath)
        Dim rootSearch As New DirectorySearcher(obDirEntry)
        Dim SearchResult As SearchResult
        Dim spn As String = strName & "@" & strDomain
        rootSearch.Filter =  _ 
          ("(&(objectCategory=user)(userPrincipalName=" & spn & "))")
        For Each SearchResult In rootSearch.FindAll 'Or FindOne
          Dim i As Integer
          'Check here - Should only return ONE result
          For i = 0 To SearchResult.Properties("cn").Count - 1
            str += SearchResult.Properties("cn")(i)
          Next
        Next
      Catch ex As Exception
        str = ex.Message
      End Try
      Return str
    End Function
    'Function finds and returns if User if in the specified user group
    
    Public Function blnFindCNinGroups(ByVal sLogonUserCN _ 
              As String, ByVal sGroup As String) As Boolean
      Try
      Dim sDirEnt As String = "LDAP://server/CN= " & _ 
          sLogonUserCN&",CN=Users,DC=Softwaremaker,DC=net"
      Dim user As DirectoryEntry = New DirectoryEntry(sDirEnt)
      Dim pcoll As PropertyCollection = user.Properties
      Dim i As Integer
      Dim s As String
      'The loop will return all Groups of which the CN is a member Of
    
      For i = 0 To pcoll("memberOf").Count - 1
        s = pcoll("memberOf")(i).ToString
        If QueryLDAP(s, sGroup) = True Then Return True
        Next
        Return False
      Catch ex As Exception
        Return False
      End Try
    End Function
    Public Function QueryLDAP(ByVal strQuery _
        As String, ByVal sGroup As String) As Boolean
      Dim obDirEntry As DirectoryEntry = Nothing
      Try
        Dim strPath As String = "LDAP://" & strQuery
        Dim s As String
        obDirEntry = New DirectoryEntry(strPath)
        Dim rootSearch As New DirectorySearcher(obDirEntry)
        Dim SearchResult As SearchResult
        For Each SearchResult In rootSearch.FindAll
          Dim i As Integer
          'Check here - Should only return ONE result
    
          For i = 0 To SearchResult.Properties("cn").Count - 1
            s += SearchResult.Properties("cn")(i)
          Next
          If s = sGroup Then Return True
        Next
      Catch ex As Exception
        Return False
      End Try
    End Function
    End Class
    '###########
  2. I am not a LDAP Query guru or expert. I am sure I am taking one step too many just to query if the user exists within the specified user group in the LDAP Directory. But I am selling idea not code so you should get the idea :)
  3. Things that can be applied includes:
    1. Using Windows Authentication in the IIS Settings.
    2. At the web.config file level:
      '########### 
      <identity impersonate="true" /> 
      <authentication mode="Windows" /> 
      <authorization> 
          <allow roles="SoftwaremakerNet\AppAUserGroup" /> 
      </authorization> 
      '########### 

Scenario B : Hybrid Forms Authentication with LDAP

LDAP Forms Flow

Please see this Microsoft link for a detailed working version.

Conclusion

The above examples only show the code implementations on the Microsoft .NET platform. You can implement the same ideas with J2EE. As long as you build LDAP capability into your application, it will work.

I hope you are able to see how you can integrate user identity management of an organizational directory into your application and map them onto your own databases. The users will still exist in the database but the management of their identity should be centralized within a LDAP Directory implementing companies' policies and securities. There are a couple of ways that True-Blue Single-Sign-On can be applied across multiple domains. However, these implement rather new and expensive technologies such as physical authentication tokens which act like your fingerprints. I will leave that topic for further discussions later.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here