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

Insight into Security Model using Principal and Identity Objects in .NET

0.00/5 (No votes)
9 Oct 2003 1  
This article introduces the concept behind Principal and Indentity objects in .NET and proposes a security model adopting these objects in a .NET application.

Introduction

The buzz word �Security� strikes me all the time when I think of designing an application. According to me, paramount importance should be given to security of any application, as hackers are always on the move. Security is something an application should possess and not be a feature.

Microsoft.NET as a technology, has given various models/patterns, towards implementing security. The patterns can be customized to suit our requirement and hence is extensible in nature.

Microsoft.NET framework provides certain objects in the name �Principal� and �Identity� objects which would be our primary area of thought. This article mainly focuses on security model by adopting �Principal� and �Identity� objects and proposes a security model for a .NET application.

What are Principal and Identity objects?

Before we proceed any further, it is important for us understand that the very intent of Principal and Identity objects is to authenticate and authorize the end user.

In simple terms, authentication is the process by which the identity of the user is verified and authorization is the process by which access to an application/feature is granted based on the identity.

Identity:

This object stores information about the user. This object encapsulates the name of the user being authenticated. We can relate this object to a data store which stores information (User Name) about a user. Identity objects are of two types.

  • WindowsIdentity: This object encapsulates the Windows login user name and the type of protocol adopted for authentication by Windows. Anytime we need to know the Windows login username, WindowsIdentity objects have the information.
  • GenericIdentity: also stores information about a user, but is used when an application needs to implement custom logon. This object can be created by specifying the User name (usually accepted via custom logon screens) and can be propagated across various application tiers for authorization purposes.

We shall be discussing more in detail about this at a later stage in this article.

Principal:

This object represents the security context for the running process or the AppDomain. Each thread has a context and it holds the principal object. Principal objects encapsulate Identity and the Role/Group membership of a user. Role based security can best be implemented with the help of Principal objects. A Principal object can be created with the help of identity and role of a user. Principal objects are very handy while implementing role based authorization for an application.

Principal objects are of two types:

  • GenericPrincipal: This object encapsulates the identity object and the role. While implementing custom logon with role based authorization, GenericPrincipal objects are to be created.
  • WindowsPrincipal: also stores identity and the Windows group membership of the user. This would very much qualify to implement role based security for an application.

Implementing role based security with Principal objects

The above theory can be well understood by considering a few of the real time scenarios.

Scenario A: Windows authentication (Identity) with roles available from database

Assumption

In this scenario, if roles are found for a user in the database, then the user is a valid user. If a role does not exist for a user in the database, then the user is anonymous and hence should not get access to any feature in the system. Also, there would be an entry for a user in the database only if role(s) are allocated.

In most cases, for an intranet/Windows based application, Windows authentication is preferred with roles being application specific and existing in a data store. Under such circumstances, we would be creating a GenericPrincipal object with Identity as Windows Logon user and roles picked up from the data store for a specific identity. This amounts to Windows identity being used for authentication and GenericPrincipal object built with roles from database used for authorization.

Meaning, we would retrieve the Windows identity, get the user name from the identity object, and query the data store for roles of the user. Once we get the user roles we can construct a GenericPrincipal object and the same can be set to the context/thread.

Now the obvious question would be, who would access the context and how does authorization happen? Let�s consider a hypothetical case wherein we need to grant access to a feature in the application based on the user role.

Authentication

This would be a seamless login from the user�s point of view. The user�s identity can be retrieved with the help of the following code:

Windows application

WindowsIdentity.GetCurrent() method would give us the identity object of the logon user

Imports System.Security.Principal
Module SecurityModule
    Sub Main()
        Dim winIdent As WindowsIdentity = WindowsIdentity.GetCurrent()
        Console.WriteLine(winIdent.Name())
    End Sub
End Module
Web application

By doing the following the identity of the Windows user is obtained

  • Set the authentication mode to Windows and deny authorization to anonymous users in Web.Config file.
  • Implement the WindowsAuthentication_OnAuthenticate event in the Global.asax file. The WindowsAuthenticationEventArgs argument received from this event gives the identity of the Windows user.
<!--Web.Config File Data-->
<authentication mode="Windows"/>
<authorization>
    <deny users="?" />  <!--Deny anonymous users -->
    <allow users="*" /> <!--Allow all users -->
<authorization/>

Global.asax code: The below event is triggered for every request made on the web application.

Sub WindowsAuthentication_OnAuthenticate(ByVal Source As Object, _
                    ByVal e As WindowsAuthenticationEventArgs)
    Dim userIdentity As String
    userIdentity = e.Identity.Name()
End Sub

Authorization

To authorize the users, we got to have roles associated with the identities and hence build the principal object. For the identity obtained from the above global.asax event, query the database and retrieve the roles. Authorization check would be performed in each page of the web application to grant or deny access to a particular feature/page in the application.

The below code explains the complete cycle involved in authorizing a user in accessing an application

Windows application

During application start-up (wherever applicable), for the identity, get the roles from the database accordingly. With the roles and the identity, the principal object needs to be created which would be accessed throughout the application. The below code explains the entire process.

Module SecurityModule
    Public genPrincipal As GenericPrincipal

    Sub Main()
        Dim ident As WindowsIdentity = WindowsIdentity.GetCurrent()
        Dim roles() As String

        roles = <retrieve from database>
        genPrincipal = New GenericPrincipal(ident, roles)
    End Sub
End Module

The above generic principal object would be accessed in each of the Windows forms in deciding whether to grant or deny access to the requested feature, as shown below:

Private Sub Form1_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load

    If genPrincipal.IsInRole("Managers") = False Then
        MessageBox.Show("You do not have access to this form")
    End If
End Sub
Web application

For a web application, before we proceed with authentication check, we got to build the principal object. And, for the web page to access this principal object, the same has to be persisted in the HTTP context. The code below explains this process:

In WindowsAuthentication_OnAuthenticate event, get the roles for the identity obtained. This completes the authentication bit. The next event to get fired just after this is the Application_AuthenticateRequest wherein the principal object needs to be built. Hence the identity and the roles obtained in WindowsAuthentication_OnAuthenticate event have to be persisted for Application_AuthenticateRequest to build the principal object. The same is performed by creating an authentication ticket out of identity-roles, and send as cookie. Now the Application_AuthenticateRequest event can access the cookie and build the Principal object as shown below

Sub WindowsAuthentication_OnAuthenticate(ByVal Source As Object, _
        ByVal e As WindowsAuthenticationEventArgs)
    
    'Note that since IIS has already performed 

    'authentication, the provided identity is used.


    Dim userIdentity As String
    Dim userRoles As String
    Dim formsAuthTicket As FormsAuthenticationTicket
    Dim httpcook As HttpCookie
    Dim encryptedTicket As String

    userIdentity = e.Identity.Name()
    useRoles =<Retrieve from Database>

    formsAuthTicket = New FormsAuthenticationTicket(1, _
        e.Identity.Name(), _
        DateTime.Now,DateTime.Now.AddMinutes(60), _
        False, userRoles)

    encryptedTicket = FormsAuthentication.Encrypt(formsAuthTicket)

    httpcook = New HttpCookie("authCookie", encryptedTicket)
    Response.Cookies.Add(httpcook)
End Sub
Sub Application_AuthenticateRequest(ByVal sender As Object, _
            ByVal e As EventArgs)

    ' Fires upon attempting to authenticate the user

    Dim formsAuthTicket As FormsAuthenticationTicket
    Dim httpcook As HttpCookie
    Dim genIdentity As GenericIdentity
    Dim roles() As String
    Dim genPrincipal As GenericPrincipal

    httpcook = Context.Request.Cookies("authCookie ")
    formsAuthTicket = FormsAuthentication.Decrypt(httpcook.Value)

    genIdentity = New GenericIdentity(formsAuthTicket.Name())
    roles = formsAuthTicket.UserData.Split("|") 'if more than one role


    genPrincipal = New GenericPrincipal(genIdentity, roles)

    'The principal object thus created would be accessed 

    'in the �application pages before allowing access 

    'to the individual functionality 

    HttpContext.Current.User = genPrincipal
End Sub

Now after the Principal object has been set, let�s move over to the web page to authorize the user. In the Form_Load event of the web page (PageA.aspx), let�s say, we have to grant access only to users of role �Managers�. The below code would perform the authorization check

Private Sub Page_Load(ByVal sender As System.Object, _
        ByVal e As System.EventArgs) Handles MyBase.Load

    Dim genPrincipal As IPrincipal

    ' Access the principal object from HTTPContext

    genPrincipal = HttpContext.Current.User

    If genPrincipal.IsInRole("Managers") = False then
        Response.Write("You do not have access to this Page.")
        ' Now the controls on the form can be hidden

        ' or the user can be redirected 

        ' to a different page

        ' < Code to implement the above goes here >

    End if
End Sub

The above would not give access to PageA.aspx, to any user who does not belong to �Managers� role/group.

In all, this security model best suits for applications which need to implement role based security with roles as defined by the application and authentication performed by Windows.

Scenario B: Custom authentication (Identity) with roles available from database

This scenario best suits applications which have users as well as roles defined in the application. Such applications will have logon page/form which accepts username and password and performs authentication. The same code as explained in scenario A except for authentication mechanism can be adopted in this scenario as well. The authentication would follow with user ID and password being validated against a data store and continues to build the generic principal object with the help of GenericIdentity and roles retrieved from the database.

Scenario C: Windows authentication (Identity) with Windows group membership as roles

This scenario best suits applications which depend on Windows for identity and roles. Identity would be the Windows username and the role would be the group membership of the Windows user. The concept of role based security remains the same and is similar to the code as mentioned in Scenario A. Instead of creating GenericPrincipal object, we got to be creating WindowsPrincipal object, by supplying the WindowsIdentity. Also, there is no need to get �roles� from a data store as we are depending on Windows group membership for roles. The check that would be performed in each page/form remains the same.

Scenario analysis

In the above scenarios, all we are doing is building the principal objects which consists of identity and roles. The pages/forms need not worry about building the Principal objects. They just need to perform �IsInRole� checks depending on the authorization rules as set by the application.

This demonstrates the clear segregation between authentication and authorization when Identity and Principal objects are adopted for implementing security. Also, throughout the life of the application, we are dealing with objects encapsulating identity, roles and are persisted across.

Conclusion

The scenarios just demonstrate the usage of Principal and Identity objects. They do not imply any rules on its usage. The scenarios were chosen as a means to understand the concepts better. In all, the Principal and Identity objects provide a means to implement security for any application in .NET.

Using the code

The code snippets mentioned throughout this article is part of the source code available for download with this article. On downloading the source code (Zip File), extract the same to web folder (wwwroot) to successfully open the solution. In the IIS Settings for the web application, make sure anonymous access is unchecked in Directory Security tab.

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