(X) Hide this
    • Login
    • Join
      • Generate New Image
        By clicking 'Register' you accept the terms of use .

Authorization in Silverlight, part 3: securing your service calls.

(3 votes)
Kevin Dockx
>
Kevin Dockx
Joined Nov 15, 2010
Articles:   19
Comments:   8
More Articles
3 comments   /   posted on Oct 26, 2011
Categories:   Line-of-Business

Introduction

Authorization comes in different forms: you’ve got authorized navigation: which role (if any) is required to access a certain View in your application, and what do you do when the user hasn’t got the correct credentials (or isn’t logged in at all)? Authorized navigation is what we tackled in the first article in this article series.

You’ve got authorizing access to certain UI elements IN a View (instead of the complete View), depending on the role of the logged in user, and deciding what to do when the user hasn’t got the correct role (for example: do you want to disable the element, or do you want to hide it completely?). That’s what we tackled in the second article of this series.

Next to that, you’ve also got authorization on your services or service methods: who can access which operation? This is what we’ll look into in this article: various means of authorizing users to access services or service methods, with WCF RIA Services, but also with regular WCF Services.

You can download the complete source code for this article here.

WCF RIA Services

We’ve already created an authentication service in the previous articles, which we simply used to sign in, get an authenticated user, and get it to the client through the WebContext. As for the server side of your application: WCF RIA Services also allows you to easily restrict access to certain service methods (or the complete domain service) through the use of attributes. Most of this is already explained in Brian Noyes’ great article series on WCF RIA Services. In part 7 he tackles authentication and authorization.

Instead of repeating all that, we’re quickly going to run through how this could be set up in our application, as (parts of) the same authentication logic will be used in the rest of this article.

A simple domain service is created, which contains three methods:

[EnableClientAccess()]
public class ExampleDomainService : DomainService
{
    [Query]
    [RequiresAuthentication()]
    public IQueryable<DummyDTO> GetAuthenticatedDummyDTOs()
    {
        List<DummyDTO> returnList = new List<DummyDTO>() 
        { new DummyDTO() { Id = 1, Description = "My dummy DTO" } };
        return returnList.AsQueryable<DummyDTO>();
    }
    [Query]
    [RequiresRole("RegularUser")]
    public IQueryable<DummyDTO> GetRegularUserDummyDTOs()
    {
        List<DummyDTO> returnList = new List<DummyDTO>() 
        { new DummyDTO() { Id = 2, Description = "My dummy DTO, for regular users" } };
        return returnList.AsQueryable<DummyDTO>();
    }
    [Query]
    [RequiresRole("Administrator")]
    public IQueryable<DummyDTO> GetAdministratorDummyDTOs()
    {
        List<DummyDTO> returnList = new List<DummyDTO>() 
        { new DummyDTO() { Id = 3, Description = "My dummy DTO, for administrators" } };
        return returnList.AsQueryable<DummyDTO>();
    }
}

All three return a (list of) DummyDTO, a very simply dummy class.

The first one is accessible for each authenticated user, the other ones require specific roles. This behavior is enabled by using the RequiresAuthentication and/or RequiresRole attributes, and decorating your methods with them. You could also decorate your domain service class with one of these attributes, which then applies to all methods in that class.

To enable roles, we need to create a custom role provider (inheriting from the RoleProvider class), of which we only need to implement one method: GetRolesForUser.

public class CustomRoleProvider : RoleProvider
{
    public override string[] GetRolesForUser(string username)
    {
        ExampleAuthenticationDomainService exDS = new ExampleAuthenticationDomainService();
        return exDS.GetUserByName(username).Roles.ToArray();
    }
}

This will return the role(s) for the current user, through the code which already exists in our authentication domain service. In the web.config file, we need to add the following code to ensure this role provider is used:

<roleManager defaultProvider="CustomRoleProvider" enabled="true" cacheRolesInCookie="true" >
  <providers>
    <add name="CustomRoleProvider" type="MVVM.WebHost.CustomRoleProvider, MVVM.WebHost"/>
  </providers>
</roleManager>

Like this, everything is in place on the server. Do note that we really need a role provider: if you don’t use one, checking if a specific user is in a role will always return false.

In the Silverlight application, three buttons are added. Clicking these buttons will load the dummy DTO’s through WCF RIA Services.  This is an example of one of these load operations (the other ones are excluded for brevity, but follow the exact same logic):

public void WCFRIAServicesAuthenticatedCall()
{
    Context.Load<DummyDTO>(Context.GetAuthenticatedDummyDTOsQuery(), (lo) =>
        {
            if (lo.HasError)
            {
                MessageBox.Show(lo.Error.Message);
                lo.MarkErrorAsHandled();
            }
            else
            {
                MessageBox.Show(lo.Entities.First().Description);
            }
        }, null);
}

If we run our application and log in, either a DTO will be returned, or an error message will be shown, depending in whether or not we’re logged in as someone with the required role. For example, this is how it looks if we log in as a regular user, and try to load data through the method which requires us to have the Administrator role:

image

 

WCF – checking the authenticated user in each operation

So far for WCF RIA Services security. Often, you’ll encounter applications in which you need to use regular WCF services, possibly together with domain services. One approach you often see is the one which is explained on MSDN here, using UserNameOverTransport (or TransportWithMessageCredential if you’re using basicHttpBinding).

In short, this boils down to using the UserNameOverTransport authentication mode in a custom binding, and checking the credentials in the service operations (or: creating a custom validator to validate the provided username & password). This must run over SSL, so you’ll need a certificate for this to work, and it requires passing in the username & password into each proxy call to a service method, by setting the ClientCredentials property on your client proxy from Silverlight.

This works nicely, but what if we want to combine this with what we’ve already created in the previous paragraph, or don’t want to manually pass in the credentials all the time? What if we have an application which mainly uses domain services, but requires some calls to WCF Services? Can we leverage what we’ve got and extend it for use in WCF?

The answer is: yes, and it’s quite easy to do. One way is to wrap the WCF Service calls in a domain service (typically through query methods or invoke operations), and leverage the WCF RIA Services authentication attributes. As such, you’re “hiding” the calls to WCF service methods behind a domain service, which has some advantages: for example, you don’t need to add service references (or manually use a channel factory), meaning you don’t need to know the exact location of your WCF services in the Silverlight client, which makes configuration easier. Next to that, the client-side developer doesn’t need to code in 2 different ways: all is executed via WCF RIA Services.

While that works and is often feasible, sometimes you might want call the method directly. One case in which this sort of requirement can come up is one where your domain services are hosted on one server and the WCF services on another one, and you need the last ones to keep working, should the domain services server be offline.

After a user has signed in through an authentication mechanism (for example: our WCF RIA Services authentication service), his credentials are contained in the HttpContext. Logically, checking this context can be done from a WCF Service as well. It contains an IsAuthenticated property (on HttpContext.Current.User.Identity), which states whether or not the user is authenticated, and it contains an IsInRole method, which can be used to check for a specific role. Have a look at the following code, from DummWCFService, a simple Silverlight-enabled WCF service:

[OperationContract]
public List<DummyDTO> GetAuthenticatedDummyDTOs()
{
    if (HttpContext.Current.User.Identity.IsAuthenticated)
    {
        return new List<DummyDTO>() 
        { new DummyDTO() { Id = 1, Description = "My dummy DTO" } };
    }
    throw new SecurityException("You haven't got access to this method");
}
[OperationContract]
public List<DummyDTO> GetRegularUserDummyDTOs()
{
    if (HttpContext.Current.User.IsInRole("RegularUser"))
    {
        return new List<DummyDTO>() 
        { new DummyDTO() { Id = 2, Description = "My dummy DTO, for regular users" } };
    }
    throw new SecurityException("You haven't got access to this method");
}
[OperationContract]
public List<DummyDTO> GetAdministratorDummyDTOs()
{
    if (HttpContext.Current.User.IsInRole("Administrator"))
    {
        return new List<DummyDTO>() 
        { new DummyDTO() { Id = 3, Description = "My dummy DTO, for administrators" } };
    }
    throw new SecurityException("You haven't got access to this method");
}

On the client side, we need to add a service reference to the WCF service. After that, we create a few buttons, and link them to commands which call the specific WCF service methods, as such (this is one of the three calls):

public void WCFAuthenticatedCall()
{
    DummyServiceReference.DummyWCFServiceClient client = 
        new DummyServiceReference.DummyWCFServiceClient();
    client.GetAuthenticatedDummyDTOsAsync();
    client.GetAuthenticatedDummyDTOsCompleted += (sender, args) =>
    {
        if (args.Error == null)
        {
            MessageBox.Show(args.Result.First().Description);
        }
        else
        {
            MessageBox.Show(args.Error.Message);
        }
    };
}

If you put a few breakpoints in your code, you’ll notice we’ve got an authenticated user on the server, as we’re authenticated through a WCF RIA Services authentication service. This means our first method, GetAuthenticatedDummyDTO’s, will be accessible for everyone who is authenticated.

In the second and third methods (GetRegularUserDummyDTOs and GetAdministratorDummyDTOs), an extra piece of code is executed, as we’re not only checking for an authenticated user, but we’re checking for a certain role as well, through HttpContext.Current.User.IsInRole. When this code called, our custom role provider (created in the first part of this article) is used, which gets the user and its roles through our WCF RIA Services authentication service.

If we run our application and log in as a regular user, we now have access to the GetRegularUserDummyDTOs and GetAuthenticatedDummyDTOs methods, which results in the following:

image

If we try to access a method for which the correct credentials are missing (for example: log in as a regular user, and click the “WCF: Administrator role call” button), it will look as such (note that depending on your Visual Studio settings, you will break into your code in debug mode when you reach an exception: this is not the case when you’re not debugging):

image

 

WCF Service – automation through attributes

While the previously explained approach does work, it has a disadvantage: you need to check for the correct credentials manually in each operation. You could of course wrap this logic in a separate class, but there’s another way: through the existing PrincipalPermission attribute, which can be found in the System.Security.Permissions namespace. Using this attribute, we’re essentially using declarative security in code.

We can change our existing service operations by using this attribute to decorate them, as such:

 [OperationContract]
 [PrincipalPermission(SecurityAction.Demand, Authenticated = true)]
 public List<DummyDTO> GetAuthenticatedDummyDTOs()
 {
     return new List<DummyDTO>() 
     { new DummyDTO() { Id = 1, Description = "My dummy DTO" } };
 }
 [OperationContract]
 [PrincipalPermission(SecurityAction.Demand, Role = "RegularUser")]
 public List<DummyDTO> GetRegularUserDummyDTOs()
 {
     return new List<DummyDTO>() 
     { new DummyDTO() { Id = 2, Description = "My dummy DTO, for regular users" } };
 }
 [OperationContract]
 [PrincipalPermission(SecurityAction.Demand, Role = "Administrator")]
 public List<DummyDTO> GetAdministratorDummyDTOs()
 {
     return new List<DummyDTO>() 
     { new DummyDTO() { Id = 3, Description = "My dummy DTO, for administrators" } };
 }

There’s one extra piece of code we need to include for this to work. By default, the PrincipalPermission attribute expects the current identity principal to be available through Thread.CurrentPrincipal. This means we need to set this, because it’s empty by default. A great place to do this is in the service constructor, as such:

 public DummyWCFService()
 {
     // to be able to use PrincipalPermission, we need to set the current
     // user to the CurrentPrincipal, as checks are executed on 
     // Thread.CurrentPrincipal, not on HttpContext.Current.User
     Thread.CurrentPrincipal = HttpContext.Current.User;
 }

No changes are necessary in the client application. If we run our application again, and try to access a method for which the correct credentials are missing (for example: log in as a regular user, and try to access a method which requires the Administrator role), you will get the same “Access is denied” popup as in the previous paragraph.

Conclusion

In this article, we’ve seen various ways to tackle securing your services. We started out by looking at WCF RIA Services security, and continued on to regular WCF services, specifically tailored to those cases in which you require a mix of technologies/frameworks, for example: a WCF RIA Services authentication service combined with regular WCF service calls.

All in all, you don’t need a lot of custom code to limit access to services or service methods to authenticated users and/or users in a specific role.

About the author

Kevin Dockx lives in Belgium and works at RealDolmen, one of Belgium's biggest ICT companies, where he is a technical specialist/project leader on .NET web applications, mainly Silverlight, and a solution manager for Rich Applications (Silverlight, Windows Phone 7 Series, WPF, Surface, HTML5). His main focus lies on all things Silverlight, but he still keeps an eye on the new developments concerning other products from the Microsoft .NET (Web) Stack. As a Silverlight enthusiast, he's a regular speaker on various national and international events, like Microsoft DevDays in The Netherlands, Microsoft Techdays in Belgium & Portugal, NDC2011, ... Next to that, he also authored a best-selling Silverlight book, Packt Publishing's Silverlight 4 Data and Services Cookbook, together with Gill Cleeren. His blog, which contains various tidbits on Silverlight, .NET, and the occasional rambling, can be found at http://blog.kevindockx.com/, and you can contact him on Twitter via @KevinDockx.


Subscribe

Comments

  • Mark2002

    Re: Authorization in Silverlight, part 3: securing your service calls.


    posted by Mark2002 on Oct 29, 2011 00:13

    Is there any downside to wrapping a WCF service in an RIA service other then the mentioned cross domain issue? Performance?

    I had not considered this and have my WCF services as "pure" WCF services using the security techniques that you descibed.  It is a pain to maintain the WCF service references (I forget to update the reference way too many times after making a service contract change :-) ) and to fiddle with the endpoints for http versus https.  Getting rid of these alone would make it worth my while to recast as RIA services.

  • MattHousley

    Re: Authorization in Silverlight, part 3: securing your service calls.


    posted by MattHousley on Apr 30, 2012 12:44
    These are a great set of articles thank you.

    When I run these under Visual Studio, they work great, but when I deploy into IIS, I get a 404 for /MVVM.WebHost/login.aspx

    Any ideas what's causing this?
  • MattHousley

    Re: Authorization in Silverlight, part 3: securing your service calls.


    posted by MattHousley on May 15, 2012 15:29

    Hi,

    I solved the '404 on the login.aspx' problem by changing the connection credentials in the Application Settings. The Web Application was set to use "Application user (pass-through authentication)" [see "Connect As" in the Edit Application dialog]. Once this was changed to a "Specific user", all was well.

    Hope that helps anyone with the same problem.

    Matt

Add Comment

Login to comment:
  *      *       

From this series