Code snippet MainWindow.cs
728 .
chaPter 33 clienT ApplicATion SerViceS
Vb
Imports System.Web.Security
Class MainWindow
Private Sub Window_Loaded(ByVal sender As System.Object,
yVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
If Membership.ValidateUser(Nothing, Nothing) Then
MessageBox.Show("User is valid")
Else
MessageBox.Show("Unable to verify user, application exiting")
Me.Close()
Return
End If
End Sub
End Class
Code snippet MainWindow.vb
Interestingly, there is no overload of the ValidateUser method that accepts no arguments;
instead, when using Windows authentication, you should use Nothing (VB) or null (C#) for the
username and password arguments. In this case, ValidateUser does little more than prime the
CurrentPrincipal of the application to use the client application services to determine which
roles the user belongs to, and by default will return true. You see later that using this method is the
equivalent of logging the user in to the application.
The preceding code snippet, and others throughout this chapter, may require you
to import the System.Web.Security namespace into this class file. You may
also need to manually add a reference to System.Web.dll in order to resolve
type references.
The client application services include what is
often referred to as an application framework
for handling security. VB has for a long
time had its own application framework for
Windows Forms Applications that is enabled
and disabled via the Application tab on the
project properties designer. This framework
already includes limited support for handling
user authentication, but it conflicts with the
client application services. Figure 33-2 shows
how you can elect to use an application-
defined authentication mode so that you can
use both the Windows application framework and the client application services in your application.
Note that this setting is available only if you are developing a Windows Forms Application in VB.
fiGure 33-2
role authorization .
729
role authorization
So far, you have seen how to enable the client application services, but they haven’t really started to
add value because the user was already authenticated by the operating system when you were using
Windows authentication for the client application. What isn’t handled by the operating system is
specifying which roles a user belongs to and thus what parts or functions within an application the
user can access. Although this could be handled by the client application itself, it would be difficult to
account for all permutations of users and the system would be impractical to manage, because every time
a user was added or changed roles a new version of the application would have to be deployed. Instead,
it is preferable to have the correlations between users and roles managed on the server, allowing the
application to work with a much smaller set of roles through which to control access to functionality.
The true power of the client application services becomes apparent when you combine the client-side
application framework with the ASP.NET Application Services. To see this, you should add a new
project to your solution using the (VB or C#) ASP.NET Empty Web Application template (under the
Web node in the New Project dialog), calling it ApplicationServices.
Right-click the newly created project in Solution Explorer and select Properties to bring up the
project properties designer. Because you will be referencing this web application from other parts
of the solution, it is preferable to use a predefined port and virtual directory with the Visual
Studio Development Server. On the Web tab, set the specific port to 12345 and the virtual path to
/ApplicationServices.
ASP.NET Application Services is a provider-based model for authenticating users, managing
roles, and storing profile (a.k.a. settings) information. Each of these components can be engaged
independently, and you can either elect to use the built-in providers or create your own. To enable
the role management service for access via client application services, add the following snippet
before the <system.web> element in the web.config file in the ApplicationServices project:
<system.web.extensions>
<scripting>
<webServices>
<roleService enabled="true"/>
</webServices>
</scripting>
</system.web.extensions>
Code snippet web.config
Because you want to perform some custom logic to determine which roles a user belongs to,
you will need to create a custom role provider, called CustomRoles, to take the place of the
default role provider. This is done by adding a new class to your project and implementing
the RoleProvider abstract class. For this role provider, you are interested only in returning a value
for the GetRolesForUser method; all other methods can be left as method stubs.
c#
public class CustomRoles: RoleProvider{
public override string[] GetRolesForUser(string username){
if (username.ToLower().Contains("nick")){
return new string[] { "All Nicks" };
730 .
chaPter 33 clienT ApplicATion SerViceS
}
else{
return new string[] { };
}
}
Code snippet CustomRoles.cs
Vb
Public Class CustomRoles
Inherits RoleProvider
Public Overrides Function GetRolesForUser(ByVal username As String) As String()
If username.ToLower.Contains("nick") Then
Return New String() {"All Nicks"}
Else
Return New String() {}
End If
End Function
Code snippet CustomRoles.vb
You now have a custom role provider and have enabled role management. The only thing missing is
the glue that lets the role management service know to use your role provider. You provide this by
adding the following roleManager node to the <system.web> element in the web.config file:
<roleManager enabled="true" defaultProvider="CustomRoles">
<providers>
<add name=" CustomRoles" type="AuthenticationServices.CustomRoles"/>
</providers>
</roleManager>
Code snippet web.config
The last thing to do is to make use of this role information in your application. You do this by first
configuring your application with the URI to use for loading role information. On the Services tab
of the ClientServices project properties (shown in Figure 33-1), enter http://localhost:12345/
ApplicationServices. Next, you need to add a call to IsUserInRole to the Window_Loaded method:
c#
private void Window_Loaded(object sender, RoutedEventArgs e){
if (Membership.ValidateUser(null, null))
{ // Commented out for brevity.
}
if (Roles.IsUserInRole("All Nicks")){
MessageBox.Show("User is a Nick, so should have Admin rights.");
}
}
Code snippet MainWindow.cs
User authentication .
731
Vb
Private Sub Window_Loaded(ByVal sender As System.Object,
ByVal e As System.Windows.RoutedEventArgs) Handles Me.Loaded
If Membership.ValidateUser(Nothing, Nothing) Then
'. Commented out for brevity .
End If
If Roles.IsUserInRole("All Nicks") Then
MessageBox.Show("User is a Nick, so should have Admin rights.")
End If
End Sub
Code snippet MainWindow.vb
To see your custom role provider in action, set a breakpoint in the GetRolesForUser method.
For this breakpoint to be hit, you have to have both the client application and the web application
running in debug mode. To do this, right-click the Solution node in the Solution Explorer
window and select Properties. From the Startup Project node, select Multiple Startup Projects
and set the action of both projects to start. Now when you run the solution, you will see that the
GetRolesForUser method is called with the Windows credentials of the current user, as part of the
validation of the user.
user authentication
In some organizations it would be possible to use Windows authentication for all user validation.
Unfortunately, in many cases this is not possible, and application developers have to come up with
their own solutions for determining which users should be able to access a system. This process
is loosely referred to as forms-based authentication, because it typically requires the provision
of a username and password combination via a login form of some description. Both ASP.NET
Application Services and the client application services support forms-based authentication as an
alternative to Windows authentication.
To begin with, you will need to enable the membership management service for access by the
client application services. Adding the <authenticationService> element to the <system
.web.extensions> element in the web.config file does this. Note that we have disabled the
SSL requirement, which is clearly against all security best practices and not recommended for
production systems.
<system.web.extensions>
<scripting>
<webServices>
<authenticationService enabled="true" requireSSL="false"/>
<roleService enabled="true"/>
Code snippet web.config
The next step is to create a custom membership provider that will determine whether a specific
username and password combination is valid for the application. To do this, add a new class,
CustomAuthentication, to the ApplicationServices application and set it to inherit from the
732 .
chaPter 33 clienT ApplicATion SerViceS
MembershipProvider class. As with the role provider you created earlier, you are just going to
provide a minimal implementation that validates credentials by ensuring the password is the reverse
of the supplied username, and that the username is in a predefined list.
c#
public class CustomAuthentication : MembershipProvider{
private string[] mValidUsers = { "Nick" };
public override bool ValidateUser(string username, string password)
{
var reversed = new string(password.Reverse().ToArray());
return (from user in mValidUsers
where string.Compare(user, username, true) == 0 &&
user == reversed
select user).Count() > 0;
}
// The rest of the implementation has been omitted for brevity
}
Code snippet CustomAuthentication.cs
Vb
Public Class CustomAuthentication
Inherits MembershipProvider
Private mValidUsers As String() = {"Nick"}
Public Overrides Function ValidateUser(ByVal username As String,
ByVal password As String) As Boolean
Dim reversed As String = New String(password.Reverse.ToArray)
Return (From user In mValidUsers
Where String.Compare(user, username, True) = 0 And
user = reversed).Count > 0
End
'The rest of the implementation has been omitted for brevity
End Class
Code snippet CustomAuthentication.vb
As with the role provider you created, you will also need to inform the membership management
system that it should use the membership provider you have created. You do this by adding the
following snippet to the <system.web> element in the web.config file:
<membership defaultProvider="CustomAuthentication">
<providers>
<add name="CustomAuthentication"
type="ApplicationServices.CustomAuthentication"/>
</providers>
</membership>
<authentication mode="Forms"/>
Code snippet web.config
settings .
733
Back on the client application, only minimal changes are required to take advantage of the changes
to the authentication system. On the Services tab of the project properties designer, select Use Forms
Authentication. This enables both the Authentication Service Location textbox and the Optional:
Credentials Provider textbox. For the time being, just specify the authentication service location as
http://localhost:12345/ApplicationServices.
Previously, using Windows authentication, you performed the call to ValidateUser to initiate the
client application services by supplying Nothing as each of the two arguments. You did this because
the user credentials could be automatically determined from the current user context in which the
application was running. Unfortunately, this is not possible for Forms authentication, so you need to
supply a username and password:
c#
private void Window_Loaded(object sender, RoutedEventArgs e){
if (Membership.ValidateUser("Nick", "kciN")){
MessageBox.Show("User is valid");
Code snippet MainWindow.cs
Vb
Private Sub Window_Loaded(ByVal sender As System.Object,
ByVal e As System.Windows.RoutedEventArgs) _
Handles Me.Loaded
If Membership.ValidateUser("Nick", "kciN") Then
MessageBox.Show("User is valid")
Code snippet MainWindow.vb
If you specify a breakpoint in the ValidateUser method in the ApplicationServices project, you will
see that when you run this solution the server is contacted in order to validate the user. You see later
that this information can then be cached locally to facilitate offline user validation.
settinGs
In the .NET Framework v2.0, the concept of settings with a User scope was introduced to allow
per-user information to be stored between application sessions. For example, window positioning
or theme information might have been stored as a user setting. Unfortunately, there was no way to
centrally manage this information. Meanwhile, ASP.NET Application Services had the notion of
profile information, which was essentially per-user information, tracked on a server, that could be
used by web applications. Naturally, with the introduction of the client application services, it made
sense to combine these ideas to allow settings to be saved via the Web. These settings have a scope
of User (Web).
As with the membership and role services, you need to enable the profile service for access by the