Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / desktop / WinForms

Implementing Application Security with Client Application Services in VS2008

4.48/5 (17 votes)
17 Jul 2008CPOL13 min read 1  
The ASP.NET provider framework for Membership, Role, and Profile functionality in .NET 3.5 is made available for non-ASP.NET consumers in the form of Client Application Services. This means that, for example, in a WinForms application, you can implement Forms Authentication as easy as in an ASP.NET

Introduction

Incorporating application security is an always returning activity. Microsoft has several mechanisms available to do this. Mostly, they’re based on the Windows security subsystem. With the advent of web-based applications, the Windows Security subsystem was not always possible or desirable. Many developers therefore implemented custom-made security subsystems into their web-applications. Microsoft responded with a built-in security subsystem in ASP.NET 2.0: the ASP.NET Membership and Role Provider framework. This framework relieved the ASP.NET developers from building their home-grown security systems.

Not only ASP.NET web applications, but WinForms client applications need security as well, not always based on the Windows Security subsystem. Here, the same trend emerged as with ASP.NET apps in building custom-made security systems and integrating them into WinForms apps. Many saw the potential of the ASP.NET Membership and Role Provider framework, and tried to integrate this into their Windows apps. Now, with Visual Studio 2008 and .NET 3.5, Microsoft responded by giving more easy access to this security subsystem for non-ASP.NET clients, like for example, WinForms apps, in the form of Client Application Services.

This article will not discuss the profile service that is also part of the Client Application Services.

Security Requirements

Let’s take a step back and discuss the typical security requirements in an application.

Information that is being managed within an organization, needs to be protected for diverse reasons. This protection can be organized and enforced in many ways. Applications that manage this information is just one level of security. This article will zoom in on this application-level security.

One of the core requirements is the need to only let authorized persons use the application in question. So, we need something to represent persons, and something that they can use to prove that they are who they say they are. Once the verification of the identity is positive, the person can use the application. In most applications, the user does not have to legitimize himself over and over again. He will be trusted during the entire usage of the application (or maybe some time limitation is applicable). Sometimes, particular parts of the application are restricted to particular persons, or better persons that have a certain function within the organization.

There are many other security requirements:

  • Accountability of users (auditing).
  • Confidentiality and integrity of data during transmission or storage (encryption).
  • Continuity and availability of data.
  • Security management.

Application Security Requirements

So, a user needs an identification “thing” in order to present himself to the security subsystem. This “thing” will differentiate him among other users. This identity must be part of the security subsystem for the application in question. The membership will determine if the user may or may not use the application. A much used identifier is the user account.

Before using a secured application, a user must get a chance to present his user account to the application (i.e., the security subsystem). A common mechanism is the logon-screen. The application will enforce that this screen will be the only way in.

But before a user that presents his user account on a logon screen is granted access to an application, we want to be sure that user is actually the “owner” of the user account. Most of the time, a “secret” is coupled with the user account. The user must supply this “secret” together with his user account. The security subsystem will verify that if the secret matches with the user account.

Sometimes the password is subject to a number of rules in terms of format to make password guessing a little bit harder. Sometimes a user makes a type while delivering the password. The security system can decide, based on an algorithm, to give a user a second chance to logon.

Once a user is authenticated for entering the application, we would also like to avoid (in most cases) that the user must always logon to the system before using some part of the application. In other words, we would like to keep some security related information “hanging around” after authentication. This can be, for example, used to store “privileges” coupled with the user account.

In order to keep the relation user account-privileges manageable, the security subsystem can introduce a security profile. This profile will then be coupled with a collection of privileges. Each user account will in turn be coupled with one or more profiles instead of direct coupling with individual privileges. The user is said to have a particular role within the application.

Now, certain parts of an application can be subject to a different set of security requirements. For example, only users belonging to the administrator role may use the update-functionality of the application. So, with the role information, we can introduce logic into our application to check whether a particular piece of code is accessible for the user in question. Moreover, instead of being “re-active”, we can take a “pro-active” approach and show or hide certain GUI elements based on the role a particular user has within that application.

Sometimes our application will exchange data with other systems (like, for example, the security subsystem). We don’t always want to send password in clear text over the wire. In order to keep the confidentiality of our data, we may need to tap into other security subsystems that are made available on the platform our application runs on (for example, encryption).

Microsoft has implementations available to incorporate these logical security mechanisms and patterns. For example, the standard .NET role-based authorsation mechanism based on Windows groups, and the Enterprise Services (COM+ role-based system). The Microsoft Patterns and Practices group has also a special application block in the Microsoft Enterprise Library, namely the Security Application Block. In Windows 2003 and XP Service Pack2, you can use the Microsoft Authorization Manager framework to make role-based applications.

Let’s see how you can implement these application security reuirements with the Client Application Services in Visual Studio 2008. I didn't supply any code with this article but you can walk through the "exercise" in the following paragraph.

Client Application Services

The Client Application Services build upon a couple of facilities that were already available in the ASP.NET 2.0 framework, but now are made available to non-ASP.NET applications.

AspNet20ProviderFramework.JPG

The existing ASP.NET providers for membership, role, and personalization are now open for WinForms apps, thanks to code in the System.Web.Extensions namespace. This is true for consumers at the server side.

ClientApplicationServicesOverview.JPG

Also, Visual Studio 2008 is enhanced to ease the configuration aspect for letting WinForms apps communicate with the Membership and Role services.

Step 1

A first step in our “exercise” is the creation of a “repository” to manage our user accounts, passwords, and roles. Because we are not focusing on the Windows security subsystem, we will choose a SQL Server database as our repository. Luckily, a utility program is made available to create such a SQL Server membership provider database: aspnet_regsql.exe. You can find this executable in the folder \WINDOWS\Microsoft.NET\Framework\v2.0.50727. This wizard will create the database (tables and stored procedures) necessary to manage the user accounts, passwords, roles, and the user/role associations.

Step 2

Although there are several stored procedures in the database you've just created it is still rather static. We need an application to manage the data inside the database. By manage I mean exercising the CRUD operation on the user, passwords and role-associations. Luckily, we don't have to write this code ourselves. This where the ASP.NET membership and the role provider framework step in.

The next step is to make an ASP.NET 3.5 web application that will host the membership provider and the role provider. When you build an ASP.NET 3.5 Web application or Web Service, you will notice several extra configuration entries in your web.config file (see later in the article). Also, a reference to the System.Web.Extensions.dll that contains the WCF Service implementations that give remote access to the Membership provider and the Role provider.

Thanks to the integration in Visual Studio, you can, in a 3.5 web-site project, open the administration part (also a web application, but only called from within Visual Studio) for the Membership/Role provider.

AspNetConfiguration_small.jpg

A worthy alternative is the Credential Manager from Idesign (Juval Löwy).

IdesignCredentialManager.JPG

Step 3

So, what do you need to change in the web configuration file?

We need an ADO.NET connection string pointing to the membership database.

XML
<connectionStrings>
  <add name="testCAS" connectionString="Data 
       Source=myMachine;Initial Catalog=testCAS…/>
</connectionStrings>

Membership and Role provider activation:

XML
<system.web>
……
    <membership defaultProvider="TestCASSqlProvider">
        <providers>

            <clear/>
            <add name="TestCASSqlProvider" 
                 type="System.Web.Security.SqlMembershipProvider" 
                 connectionStringName="testCAS" 
                 applicationName="testCAS"/>
        </providers>
    </membership>
    <roleManager enabled="true" defaultProvider="TestCASSqlRoleProvider">
        <providers>

            <clear/>
            <add name="TestCASSqlRoleProvider" 
                 connectionStringName="testCAS" 
                 applicationName="testCAS" 
                 type="System.Web.Security.SqlRoleProvider"/>
        </providers>
    </roleManager>
….
</system.web>

Forms authentication activation:

XML
<system.web>

….
    <authentication mode="Forms" />
….
</system.web>

Activation of the Authentication and Role (WCF) Service.

XML
<system.web.extensions>
    <scripting>
        <webServices>
            <authenticationService enabled="true" requireSSL="false"/>

            <roleService enabled="true"/>
        </webServices>
    </scripting>
</system.web.extensions>

Step 4

Now, it is time to configure the WinForms (or WPF) application that will be using the Authentication and Role service hosted in our ASP.NET application. Through the new tab “Services” in the project properties in Visual Studio 2008, you can easily do this. Checking the radio-button "Forms Authentication" will add a reference to System.Web.Extension. This assembly contains next to all server logic, also all logic for the consumer side. There is also a textbox where you can enter the URL of the ASP.NET web application that acts as an Authentication service and a Role service host. You can also specify a form, implementing a special interface, that will act as a logon screen. This can be handy to implement certain scenarios like initial logon.

XML
<system.web>
    <membership defaultProvider="ClientAuthenticationMembershipProvider">
      <providers>
        <add name="ClientAuthenticationMembershipProvider" 
           type="System.Web.ClientServices.Providers.
                       ClientFormsAuthenticationMembershipProvider, 
                 System.Web.Extensions, Version=3.5.0.0, …" 
           serviceUri="http://myMachine/testCAS/Authentication_JSON_AppService.axd" 
           credentialsProvider="TestCASClient.LoginFormCAS,TestCASClient" …/>

      </providers>
    </membership>
    <roleManager defaultProvider="ClientRoleProvider" enabled="true">
      <providers>
        <add name="ClientRoleProvider" 
             type="System.Web.ClientServices.Providers.ClientRoleProvider, 
                   System.Web.Extensions, Version=3.5.0.0, …" 
             serviceUri="http://myMachine/testCAS/Role_JSON_AppService.axd" …." />
      </providers>

    </roleManager>
  </system.web>

ServicesTab.JPG

Step 5

Don’t forget to add a reference to System.Web in order to use the membership and role namespace. Although, System.Web.Extension contains the real code. The consumer code also uses the standard membership and role namespaces to programmatically authenticate the user or to determine if a user belongs to a certain role. So enough configuration. Let's program something.

Under The Hood

The API to use CAS is very straightforward. If you have ever programmed with the ASP.NET membership you will see ... that it is the same API. This is of course the core of the provider model. So, for example, let's see what happens when we execute the following statement,

VB
Dim valid As Boolean = _
  System.Web.Security.Membership.ValidateUser("Alice", "AliceHer1pwd!")

The membership provider framework will determine through the specified configuration which concrete implementation will have to be instantiated. In our case, it will be the ClientFormsAuthenticationMembershipProvider in the System.Web.ClientServices.Providers namespace. The code will construct and send an HTTP POST request to the authentication service to ask if the supplied user account and password are valid. The message body doesn’t contain SOAP though, but is written in JSON format.

The HTTP message (explicitly no SSL is used!) if the statement is executed, is as follows:

POST /testCAS/Authentication_JSON_AppService.axd/Login HTTP/1.1
Content-Type: application/json; charset=utf-8
Host: myStation
Content-Length: 71
Expect: 100-continue
Proxy-Connection: Keep-Alive

{"userName":"alice","password":"AliceHer1pwd!","createPersistentCookie":false}

The URI to where this post is sent to is retrieved from the app.config file we just created with the help of the Services tab in Visual Studio 2008. Visual Studio 2008 adds a piece to the URL we specified, and refers to the Membership (authentication) and Role service in the ASP.NET 3.5 application we created to host those services. The suffix _AppService.axd will trigger an HTTP-handler in the ASP.NET application. This handler will, based on the MIME-type, determine if it is a SOAP request or a JSON request. Based on other parts of the URL, the authentication service or role services will be called to handle the request.

XML
<httpHandlers>
<remove verb="*" path="*.asmx"/>
<add verb="*" path="*.asmx" 
  validate="false" 
  type="System.Web.Script.Services.ScriptHandlerFactory, 
        System.Web.Extensions, Version=3.5.0.0, 
        Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add verb="*" 
  path="*_AppService.axd" 
  validate="false" 
  type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, 
        Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>
<add verb="GET,HEAD" 
path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, 
System.Web.Extensions, 
Version=3.5.0.0, 
Culture=neutral, 
PublicKeyToken=31BF3856AD364E35" 
validate="false"/>

</httpHandlers>

Deep down in code, we end up in the System.Web.Security.SqlMembershipProvider namespace. If we look at the SQL traffic, we will see the following statements being sent to the SQLServer:

Retrieval password:

SQL
exec dbo.aspnet_Membership_GetPasswordWithFormat @ApplicationName=N'testCAS', 
    @UserName=N'alice',@UpdateLastLoginActivityDate=1, 
    @CurrentTimeUtc='2008-05-30 07:12:18:760'

Some accounting after the verification of the password:

SQL
exec dbo.aspnet_Membership_UpdateUserInfo @ApplicationName=N'testCAS', 
    @UserName=N'alice',@IsPasswordCorrect=1,@UpdateLastLoginActivityDate=1……

The HTTP response of the web-method looks like this:

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
Date: Fri, 30 May 2008 06:55:36 GMT
X-Powered-By: ASP.NET
Set-Cookie: .ASPXAUTH=28818033DF1ACC3638F96196B78D60AA44BB2F734F…..; path=/; HttpOnly
Cache-Control: private, max-age=0
Content-Type: application/json; charset=utf-8
Content-Length: 10

{"d":true}

The body contains the answer to our authentication request, again in JSON format. The ClientFormsAuthenticationMembershipProvider will “analyse” this and act upon it. When the user account/password is valid, the authentication code will also create two objects that will represent the logged-on user: ClientRolePrinciple and ClientFormsIdentity. With, for example, the RolePrincipal, you can ask the “system” if the particular logged-on user belongs to a certain role. This will form the basis for programmatically authorization code (re-active or pro-active). You can use this functionality by executing a cast on System.Threading.Thread.CurrentPrincipal (and System.Threading.Thread.CurrentPrincipal.Identity for the FormsIdentity).

VB
Dim RolePrincipal As System.Web.ClientServices.ClientRolePrincipal = _
    DirectCast(System.Threading.Thread.CurrentPrincipal,  _
    System.Web.ClientServices.ClientRolePrincipal)

Dim FormsIdentity As System.Web.ClientServices.ClientFormsIdentity = _
    DirectCast(System.Threading.Thread.CurrentPrincipal.Identity,  _
    System.Web.ClientServices.ClientFormsIdentity)

Another scenario I would like to track is to see what happens when you ask if a user belongs to a role. Programmatically, this it is executed through RolePrincipal.IsInRole(“Manager”). This time the ClientRoleProvider in the System.Web.ClientServices.Providers namespace is responsible for establishing the web request (again in JSON). This will result in sending the following HTTP POST request. So, first, all roles for a particular user are retrieved.

POST /testCAS/Role_JSON_AppService.axd/GetRolesForCurrentUser HTTP/1.1
Content-Type: application/json; charset=utf-8
Host: myStation
Cookie: .ASPXAUTH=28818033DF1ACC3638F96196B78D60AA44BB2……
Content-Length: 0

The role service will trigger a call to SQL Server.

SQL
exec dbo.aspnet_UsersInRoles_GetRolesForUser @ApplicationName=N'testCAS',
    @UserName=N'alice'

The answer will be:

HTTP/1.1 200 OK
Server: Microsoft-IIS/5.1
Date: Fri, 30 May 2008 06:55:36 GMT
X-Powered-By: ASP.NET
Cache-Control: private, max-age=0
Content-Type: application/json; charset=utf-8
Content-Length: 31

{"d":["Manager"]}

Then, the code (consumer side) will determine if the supplied role in the method call is in the retrieved list of roles (in our case, the user belongs to only one role). This information will be stored in our "session", i,e., RolePrincipal. So, subsequent calls will not result in calls to the service (but there is a timeout element involved!).

I only showed you some scenarios. Some other scenarios that you could investigate are:

  • Logoff
  • Off-line logon
  • Identity flow to back-end services
  • Confidentiality (SSL enabled web app)
  • SQL Server and culture-sensitive characters
  • Etc.

Conclusions & Recommandations

I think most application security requirements can be tackled with the implementation in Client Application Services.

  • You can use non-Windows user-accounts to identify users of your application.
  • There is a role-based security set-up.
  • You have an Administration program to manage users, their passwords, and their roles.
  • Password verification is done by the SQLmembership provider.
  • Once authenticated, a "session" is kept to hold on to security related info like the roles of a user.
  • There is an out-of-the box retry mechanism if you type in the wrong password.
  • You can configure the desired password strength.
  • With conditional statements while using the API, you can shield parts of your application from users depending on their role.
  • The authentication service can be configured to use SSL (explicitly disabled in the article).
  • Etc.

The Membership and Role service in the Client Application Services in Visual Studio 2008 gives a WinForms (or WPF) developer all the tools necessary to implement the most common security requirements. With some configuration work, you can unlock a wealth of functionality you don’t have to program yourself. The API to tap into this system is very straightforward.

So, next time you need to implement application security into you WinForms app, give Client Application Services a go, and see if it is sufficient for you. If so, it will save you a lot of development time.

References and Further Reading

  • Daniel Moth , Client Application Services with Visual Studio 2008, http://channel9.msdn.com/posts/DanielMoth/Client-Application-Services-with-Visual-Studio-2008/
  • Mike Taulty's Blog , Client Application Services , http://mtaulty.com/CommunityServer/blogs/mike_taultys_blog/archive/2007/06/03/9463.aspx
  • Juval Lowy Manage Custom Security Credentials the Smart (Client) Way , http://www.code-magazine.com/Article.aspx?quickid=0511031
  • Unify Windows Forms and ASP.NET Providers for Credentials Management , Juval Lowy, http://msdn.microsoft.com/en-us/magazine/cc163807.aspx
  • Michele Leroux Bustamante ,Securing ASP.NET Applications - With Less Code ,http://msdn.microsoft.com/en-us/library/aa479008.aspx
  • MSDN, Client Application Services, http://msdn.microsoft.com/en-us/library/bb384297.aspx

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)