Click here to Skip to main content
Click here to Skip to main content

Authentication and Authorization with ASP.NET Identity 2.0 for WCF Services

, 14 Aug 2014 CPOL
Rate this:
Please Sign up or sign in to vote.
Real world WCF project structure, Authentication and Authorization

Introduction

This is an extension of my previous article "WCF for the Real World, Not Hello World". So I would presume you have read it before reading this or got used to similar project structures, typically, for each service, you would establish 3 assemblies for contracts, implementation and client API.

Background

"WCF for the Real World, Not Hello World" had demonstrated how to efficiently and effectively manage WCF service development in enterprise environment through team works in order to rapidly develop and deliver decent Web services. And the article had also explained WHY in addition to HOW.

This article covers the following:

  1. Construct integration tests using xUnit and IIS Express.
  2. Introduce authentication and authorization though ASP.NET Identity 2.0.

Prerequsites :

  • Visual Sutdio 2013
  • xUnit through NuGet
  • xUnit runner for Visual Studio

Make Integration Tests Easier

I would like to run integration tests either in IDE or in a standalone runner, and the services should be launced and stopped automaitically, without the need of running things as Administrator.

Basic Project Structure

Project sturcture

In addition to service codes in folder RealWorldServices, I create a WCF Application project "WcfService1" to contain all service assemblies, for example, RealWorldService and RealWorldImp. Please notice that I had deleted all the C# codes generated by IDE, so this project is just a thin "main program"/booster to launch services in IDE and IIS Express. As you might had seen in  "WCF for the Real World, Not Hello World"  , having a WCF Application project is not madatory for hosting services in IIS

Steps:

  1. Create a WCF Application project.
  2. Remove all C# codes generated by IDE.
  3. Add references to WCF service projects, for example RealWorldService and RealWorldImp.

The Web.config is telling IIS to activate the services without using SVC files:

  <system.serviceModel>
    <serviceHostingEnvironment>
      <serviceActivations>
        <!--This is to replace the standalone svc file whic is the legacy of asp.net web app.-->
        <add relativeAddress = "RealWorldServices/RealWorld.svc" service = "Fonlow.Demo.RealWorldService.Service1"/>
      </serviceActivations>
    </serviceHostingEnvironment>
    <services>
      <service name="Fonlow.Demo.RealWorldService.Service1" behaviorConfiguration="authBehavior">

        <!-- Service Endpoints. A Service may provide multiple endpoints -->
        <!-- Not need to define host. Relative  -->
        <endpoint address="" binding="basicHttpsBinding" contract="Fonlow.Demo.RealWorldService.IService1" bindingConfiguration="httpsBindingConfig">
          <!-- 
              Upon deployment, the following identity element should be removed or replaced to reflect the 
              identity under which the deployed service runs.  If removed, WCF will infer an appropriate identity 
              automatically.
          -->
          <identity>
            <dns value="localhost" />
          </identity>
        </endpoint>

When you run WcfService1, it will be launched by IIS Express by default, and the services mentioned in web.config will be activated.

When you are developing more services, you may continue to add references to the WcfService1 project.

Hints:

The first time when you run WcfService1 through Visual Studio, the service will be registered/updated in
%userprofile%\documents\iisexpress\config\applicationhost.config. Walking through this file you will have fair a bit idea about how to configure.
 

More Robust Client Proxies

If you had done WCF programming long enough, likely you have encountered a long standing WCF defect which makes WCF Clients in .NET could not report some errors properly:

"The communication object, System.ServiceModel.Channels.ServiceChannel, cannot be used for communication because it is in the Faulted state."

No specific error info was disclosed in the exception.

The problems and the solutions are detailed in my blog "Proper Disposal of WCF Channels against a WCF Defect. Follow up". So after generating proxy classes using svcutil.exe, it is better to wrap the proxy client classes with "Safe Channel".

    /// <summary>
    /// http://webandlife.blogspot.com/2012/08/proper-disposal-of-wcf-channels-against.html
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <remarks>Better to put this into another component</remarks>
    public class SafeChannel<T> : IDisposable where T : ICommunicationObject, IDisposable
    {
        public SafeChannel(T channel)
        {
            Instance = channel;
        }
        public static IDisposable AsDisposable(T client)
        {
            return new SafeChannel<T>(client);
        }
        public T Instance { get; private set; }
        bool disposed;
        public void Dispose()
        {
            Dispose(true);
            GC.SuppressFinalize(this);
        }
        private void Dispose(bool disposing)
        {
            if (!this.disposed)
            {
                if (disposing)
                {
                    Close();
                }
                this.disposed = true;
            }
        }
        void Close()
        {
            bool success = false;
            try
            {
                if (Instance.State != CommunicationState.Faulted)
                {
                    Instance.Close();
                    success = true;
                }
            }
            finally
            {
                if (!success)
                    Instance.Abort();
            }
        }
    }

    public class RealWorldProxy : SafeChannel<Fonlow.RealWorldService.Clients.Service1Client>
    {
        public RealWorldProxy(string endpointName)
            : base(new Fonlow.RealWorldService.Clients.Service1Client(endpointName))
        {

        }
    }

Integration Tests with Automatic Tear up and down of Services

There are quite a few ways to construct integration tests by developers:

  1. Host the services in IIS, and run the integration test suit either in VS IDE or a standalone runner. And you acan attach service codes to respective w3wp.exe instance for debugging.
  2. Create service projects in A.Sln, and the test suilts in B.Sln, so you can step through both the service hosted in IIS Express launched by VS IDE, and the client codes.

However, I would like to put the test suits and the service codes in the same VS solution file, and run tests with automatic tear up and down of the services.

Generally I would write integration test suits on NUnit or xUnit, rather than MS Test, so I could run the test suits during deployment without Visual Studio installed.

    public class IntegrationTest : IUseFixture<IisExpressFixture>
    {
        const string realWorldEndpoint = "DefaultBinding_RealWorld";

        [Fact]
        public void TestGetData()
        {
            using (RealWorldProxy client = new RealWorldProxy(realWorldEndpoint))
            {
                client.Instance.ClientCredentials.UserName.UserName = "test";
                client.Instance.ClientCredentials.UserName.Password = "tttttttt";
                Assert.True(client.Instance.GetData(1234).Contains("1234"));
            }

            ...

        }

The IIsExpressFixture is responsible for starting IIS Express at the beginning and stoping the service after all test cases in class IntegrationTest are finished.

I would run all tests regularly including unit tests and integration tests.

When debugging the run time behaviors of the service, I would execute the service in IDE, and run the integration tests in xUnit Runner.

Authentication and Authorization with ASP.NET Identity 2.0

As of today in July 2014, there have been quite lot articles published about usng ASP.NET MembershipProvider and RoleProvider in WCF services, however, there is few about using ASP.NET Identity 2.0. 

ASP.NET and WCF had provided rich sets of security features, there are variety of choices for authentication and authorization. Here I would like to introduce the simple one with username and password, since this is the default way in ASP.NET MVC. 

If you are building ASP.NET MVC 5 applications with Identity 2.0 for B2C use cases and will provide related Web services to external developers for B2B use cases, you would probably want to share the same machanism for related WCF services in your multi-tier architecture.

Assigning Roles to Operations

Through declarative programming, you could assign some roles to each service implementation:

    public class Service1 : IService1 
    {
        public string GetData(int value)
        {
            System.Diagnostics.Debug.WriteLine("GetDataCalled");
            if (value == 666)
                throw new FaultException<Evil666Error>(new Evil666Error() { Message = "Hey, this is 666." });

            return string.Format("You entered: {0}", value);
        }

        [PrincipalPermission(SecurityAction.Demand, Role="Customer")]       
        public CompositeType GetDataUsingDataContract(CompositeType composite)
        {

If you have done WCF authorization with RoleManager, you will see the code base is the same with PrincipalPermissionAttribute. In other words, your legacy codes depending on RoleManager don't need to be changed.

Adapting Identity Model in Codes

using System;
using System.IdentityModel.Selectors;
using System.ServiceModel;
using System.Security.Principal;
using System.Diagnostics;

//It is better to put the classes into a shared component
namespace Fonlow.Web.Identity
{
    public class IdentityValidator : UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            Debug.WriteLine("Check user name");
            if ((userName != "test") || (password != "tttttttt"))
            {
                var msg = String.Format("Unknown Username {0} or incorrect password {1}", userName, password);
                Trace.TraceWarning(msg);
                throw new FaultException(msg);//the client actually will receive MessageSecurityException. But if I throw MessageSecurityException, the runtime will give FaultException to client without clear message.
            }

        }
    }

    public class RoleAuthorizationManager : ServiceAuthorizationManager
    {
        protected override bool CheckAccessCore(OperationContext operationContext)
        {
            // Find out the roleNames from the user database, for example, var roleNames = userManager.GetRoles(user.Id).ToArray();

            var roleNames = new string[] { "Customer" };

            operationContext.ServiceSecurityContext.AuthorizationContext.Properties["Principal"] = new GenericPrincipal(operationContext.ServiceSecurityContext.PrimaryIdentity, roleNames);
            return true;
        }

    }

}

If you have your own user and role tables, it should be easy for your to replace the implementation, and you can even utilize ASP.NET Identity 2.0.

Wire Authentication and Authorization in Web.Config

    <behaviors>
      <serviceBehaviors>
        <behavior name="authBehavior">
          <serviceCredentials>
            <userNameAuthentication userNamePasswordValidationMode="Custom"  customUserNamePasswordValidatorType="Fonlow.Web.Identity.IdentityValidator,Fonlow.RealWorldImp" />
          </serviceCredentials>
          <serviceAuthorization principalPermissionMode="Custom" serviceAuthorizationManagerType="Fonlow.Web.Identity.RoleAuthorizationManager,Fonlow.RealWorldImp">

          </serviceAuthorization>

        </behavior>
      </serviceBehaviors>
    </behaviors>

And this line wires the behavior to the service.

      <service name="Fonlow.Demo.RealWorldService.Service1" behaviorConfiguration="authBehavior">

As you can see, comparing with the usage of MembershipProvider and RoleProvider, the codes and config become shorter and more elegant.

Adapting ASP.NET Identity 2.0

I would presume you have some skills in ASP.NET MVC 5 with ASP.NET Identity 2.0 and Entity Framework, so I won't repeat what you had already known and what available in many articles.

    public class IdentityValidator : UserNamePasswordValidator
    {
        public override void Validate(string userName, string password)
        {
            using (var context = new IdentityDbContext())
            {
                using (var userManager = new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(context)))
                {
                    var user = userManager.Find(userName, password);
                    if (user == null)
                    {
                        var msg = String.Format("Unknown Username {0} or incorrect password {1}", userName, password);
                        Trace.TraceWarning(msg);
                        throw new FaultException(msg);//the client actually will receive MessageSecurityException. But if I throw MessageSecurityException, the runtime will give FaultException to client without clear message.
                    }
                }

            }

        }
    }

    public class RoleAuthorizationManager : ServiceAuthorizationManager
    {
        protected override bool CheckAccessCore(OperationContext operationContext)
        {
            using (var context = new IdentityDbContext())
            using (var userStore = new UserStore<ApplicationUser>(context))
            {
                using (var userManager = new UserManager<ApplicationUser>(userStore))
                {
                    var identity =operationContext.ServiceSecurityContext.PrimaryIdentity;
                    var user = userManager.FindByName(identity.Name);
                    if (user == null)
                    {
                        var msg = String.Format("Unknown Username {0} .", user.UserName);
                        Trace.TraceWarning(msg);
                        throw new FaultException(msg);
                    }

                    //Assign roles to the Principal property for runtime to match with PrincipalPermissionAttributes decorated on the service operation.
                    var roleNames = userManager.GetRoles(user.Id).ToArray();//users without any role assigned should then call operations not decorated by PrincipalPermissionAttributes
                    operationContext.ServiceSecurityContext.AuthorizationContext.Properties["Principal"] = new GenericPrincipal(operationContext.ServiceSecurityContext.PrimaryIdentity, roleNames);

                    return true;
                }
            }

        }

    }

As you can see comparing 2 different implementations of class IdentifyValidator on IdentityModel, the whole structures are the same with or without Identity 2.0, 

And you have probably recognized that IdentityDbContext is the DbContext class generated by VS IDE when creating the MVC 5 project. It doesn't sound elegant that your WCF projects depend on the MVC 5 project. So I would move the respective models and DbContext out of the MVC 5 project into a project called MyCompany.Security.Identity. Then the MVC 5 project and WCF projects could share the same auth machanism.

Remarks:

You won't see the above codes with Identity 2.0 in the demo codes attached in this article, since the extra dependencies on Identity 2.0, Entity Framework and the SQL database should be available in your MVC 5 project, and it is better to keep the demo codes small serving as demo.

Points of Interest

Over years I have written a lot WCF services, reviewed/fixed legacy codes left by other developers, and consumed Web services from other vendors. I find malpractices or inefficient practices resulting in dirty solutions were wide spread, consuming too much of company's wallets and developers' lifes unnecessarily.

While I firmly believe the essenses of the problems are in project management and company culture, particularly the inappropriate mindsets when employing Agile practices or Scrum framework, I consider that developers themself should be thrilled to write least amount of codes in least complexity, and automate their development works as much as possible.

WCF and MVC are very advanced, comprehensive and mature, we should study more in depth, rather than write clumsy codes that work but generate huge technical debts.

Entity Framework and Identity 2.0 make things even better, so you could easily get rid of the clumsiness of legacy providers. It took me very little efforts to move the database from the default MS SQL to MySql.

License

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

Share

About the Author

Zijian
Software Developer
Australia Australia
I started my IT career in programming on different embedded devices since 1992, such as credit card readers, smart card readers and Palm Pilot. Programming on the hardware was really fun, feeling like driving the hardware directly.
 
Beside technical works, I enjoy reading literatures, playing balls, cooking and gardening.

Comments and Discussions

 
GeneralMy vote of 5 PinmemberHumayun Kabir Mamun14-Aug-14 20:04 
QuestionPics are ok now PinmemberL Hills4-Aug-14 1:41 
Questionpictures are missed PinmemberHarun Çetin1-Aug-14 1:57 
GeneralFigures not showing up PinmemberBigTimber@home31-Jul-14 11:30 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

| Advertise | Privacy | Mobile
Web03 | 2.8.141022.2 | Last Updated 14 Aug 2014
Article Copyright 2014 by Zijian
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid