-->

01/01/2012

Basics of Single Sign on (SSO)


SSO: Single sign-on (SSO)is a session/user authentication process that permits a user to enter one name and password in order to access multiple applications. The process authenticates the user for all the applications they have been given rights to and eliminates further prompts when they switch applications.


When i looked at a SSO infrastructure, one thing i understood is,
"It is very secure in execution and very complex to understand in first look".
So, with the knowledge i acquired through various sources, i decided to create a small POC demonstrating what we have in SSO and how it works, starting from very basics.

Typically SSO comes into the picture when there is a user switching between different application domains.
Example: we are working in IT companies, and basically IT companies wont take burden of payroll and insurance process. So they will hire a vendor to take care of these activities.
In that case there will be a links to those sites in your company sites.
If you observe the Urls, they are of different domains (http://xxxxx.com). But when you navigate from your company site to vendor site, it will not ask for UserId and password.
Does this mean UserId and Password for both the sites are same?
No, In most of the real time scenarios, they will be different.
Now we have a question how this authentication is taken care?
Ans: One of the Answers is SSO.

Lets demonstrate and verify its functionality step by step.

Step 1: Create an forms authentication application "Main Application", representing our company site.
Why Forms Authentication, why not Windows authentication? 
You can use Any one. But ultimately the SSO works on claims based authentication irrespective of what authentication you use in your application.
My MainApp (SSOBase1) has a MainLogin page and a MainApppage.

Step 2: Create a second Forms application which can represent a Vendor or Client application.
Step 3: In order to accommodate forms authentication we need to have a database to maintain credentials.
So create a DB supporting infrastructure for your application.
In real time scenarios, you will have different databases for both the applications.
But for simplicity i created a single database but maintained this isolation in application. Lets see how.
Database :
 Main App DBML :
 Client App DBML:
 Now observe the dbml files care fully. Main app deals nothing with Client1 application DB tables and viseversa.
So we are maintaining complete isolation between DB infrastructure of Main app and client apps.

Main app / STS DB will have following information:
1. User credentials for Main application.
2. List of Client / Vendor apps serving our Main Application.
3. Which user and their accessibility to Client / Vendor application.
Client App DB will have following Information:
1. Client Application credentials.
2. Mapping between STS Security Token or Claim and the Client application credentials.

Step 4: Now implement respective Forms authentication so that no unauthenticated users can enter individual apps.
Main app:


Client App 1:
Look at the Urls carefully. You can see the applications are redirecting Unauthenticated user to respective Login pages for authentication.

Step 5: Now coming to user switching between cross domain applications. SSO uses a authentication provider typically called as STS (Secure Token System).

STS: This is the system which will authenticate the user and its authentication will be trusted by all the parties both Main application and Vendor/Client applications. Once a user has been authenticated by STS, all the client applications served by this STS will trust the user and no further authentication should be prompted.

STS can be part of Main App or a Individual service or App.

Now lets see how we have handled this in our application. if you observe clearly the snapshot of Main app, we have a web service "Authenticator.asmx".
This acts like a STS system and usually takes the responsibilities:
1. Authenticating users
2. Verifying what all applications he is authorized to.
3. Providing specific Security Token or Claims (Pieces of information which client app will agree as Authentication)

Look at the different methods:
Authenticator() : Constructor accepting logged in userId.
AuthenticateUser(): Method which will verify the user authentication for Main App.
GetAppAccessDetails(): This method will collect information of all client or vendor sites for which current user is authenticated, from STS / Main App's DB.
AuthenticateClientUrl(): This method will provide required security token or claims required by Vendor / Client application.

Step 6: Only changes a vendor system has to do is, while logging it have to look out whether user is authenticated by STS or not.
If yes, allow him to Client HomePage. If no Redirect him to Login page of Client Application.
In order to implement this, i added a query string with name "IsAuthenticatedBySSO", while redirecting to client application. This action is been taken care by  AuthenticateClientUrl() method of STS.
Just follow the below pieces of code from both STS and Client home page.

STS:
namespace SSOBase1
{    
    [WebService(Namespace = "http://tempuri.org/")]
    [WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
    [System.ComponentModel.ToolboxItem(false)]
    
    public class Authenticator : System.Web.Services.WebService
    {
        string _userId;
        public Authenticator(string UserId)
        {
            _userId = UserId;
        }

        [WebMethod]
        public bool AuthenticateUser(string password)
        {            
            SSODBDataContext db = new SSODBDataContext();
            var psw = (from c in db.MainAppCredentials
                      where c.Userid == _userId
                      select c.Password).ToList();
            string strPsw = psw[0].ToString();
            return strPsw == password ? true : false;                    
        }

        [WebMethod]
        public DataTable GetAppAccessDetails()
        {
            SSODBDataContext db = new SSODBDataContext();
            var results=(from a in db.Apps
                             join ua in db.UserAppMaps on a.AppId equals ua.AppId
                             where ua.MUserId == _userId
                             select a).ToList();
            
            DataTable dtRes=new DataTable();
            dtRes.Columns.Add("ApplicationName");        
            dtRes.Columns.Add("ApplicationURL");
            foreach(var c in results)
            {
                DataRow dr=dtRes.NewRow();
                dr[0]=c.AppName;
                dr[1]=c.AppUrl;
                dtRes.Rows.Add(dr);
            }
            return dtRes;
        }

        [WebMethod]
        public string AuthenticateClientUrl(string clientUrl)
        {
            return clientUrl + "?UserId=" + _userId + "&IsAuthenticatedBySSO=1";
        }

    }
}

Client Home Page:
namespace SSOTestClientApp1
{
    public partial class Client1MainPage : System.Web.UI.Page
    {
        string struser;
        protected void Page_Load(object sender, EventArgs e)
        {
            if (Request.QueryString["UserId"] == null && Session["FromClient1Login"] == null)
                FormsAuthentication.RedirectToLoginPage();
            else
            {
                if (Session["FromClient1Login"] != null)
                {
                    struser = Session["Userid"].ToString();
                    lblMessage.Text = "Welcome " + struser + System.Environment.NewLine + " This Login is from Client 1 login";
                }
                else if (Request.QueryString["IsAuthenticatedBySSO"].ToString()== "1")
                {
                    struser = Request.QueryString["UserId"].ToString();
                    SSOClient1DBDataContext db = new SSOClient1DBDataContext();
                    var _appSpecificUserName = (from u in db.Client1Maps
                                                where u.MainAppUserId == struser
                                                select u.C1AppUserId).ToList();
                    struser = _appSpecificUserName[0].ToString();
                    lblMessage.Text = "Welcome " + struser + System.Environment.NewLine + " This Login is from SSO";
                }
            }
        }
    }
}
In client home page:
1. The first highlighted part will verify if user is authenticated by either Client1 Login page nor STS. If yes, it will redirect user to Client1 Application login page.
2. The second highlighted part will display message when user is authenticated from Vendor or Client login page directly.
3. The third one will display message when user is been authenticated by STS.

Step 7:Now, lets hit the Vendor Main Page.
lets log in with Vendor credentials.
Now Hit Main Application.
Enter Main application.
Now How this Hyperlinks appear on Main application home page.
Look at the code below:
namespace SSOBase1
{
    public partial class MainAppPage : System.Web.UI.Page
    {
        protected void Page_Load(object sender, EventArgs e)
        {
            if (Session["UserId"] == null)                
                FormsAuthentication.RedirectToLoginPage();
            else
            {

                string _userId = Session["UserId"].ToString();
                lblMessage.Text = "Welcome " + _userId + " to Main Application.";
                Authenticator svc = new Authenticator(_userId);
                DataTable dtResult = svc.GetAppAccessDetails();
                foreach (DataRow dr in dtResult.Rows)
                {
                    HyperLink _hyperlink = new HyperLink();
                    _hyperlink.Text = dr["ApplicationName"].ToString();
                    _hyperlink.NavigateUrl = svc.AuthenticateClientUrl(dr["ApplicationURL"].ToString());
                    divLinks.Controls.Add(_hyperlink);
                    divLinks.Controls.Add(new LiteralControl("<BR>"));
                }
            }
        }
    }
}
Now Click on Client1 Link:
Look at the message and cross check with the Client Home page code, you will see how the LoginUser mapping is done and how the Client application has accepted the token provided by STS.


But i pick up Query string for security token, some one may pick up some thing else.
So SSO has standardized the token approach by taking SAML (Security Assertion Markup Language).
We will discuss all the standards and the real time implementation in next post.

Now we clearly understood the basic skeleton behind SSO.
Code:
Click Here
Is it helpful for you? Kindly let me know your comments / Questions.

7 comments:

  1. The SSO can also be done using tag in .NET web.config files. This option does not include database for storing authentication ticket for the user. It is quite easy and more maintainable

    ReplyDelete
  2. Article is very good. It will be helpful if you share the source code to understand better.

    Thanks,
    Shekhar

    ReplyDelete
  3. Very interesting solution. How would user on client site log off? Does the user go back to main site to log off?

    Can you please add me as friend on 'keep and share' so i can take a peek at the source. my username is 'headros'.

    ReplyDelete
    Replies
    1. Thanks Bradley. you can join as member of this blog, so that you can get the updates.

      Client is free to log of individual applications, he doesn't need to go back to main site.

      Delete