Click here to Skip to main content
15,867,568 members
Articles / Security

WCF REST 4.0 Authorization with Form Based Authentication (SetAuthCookie)

Rate me:
Please Sign up or sign in to vote.
4.88/5 (24 votes)
19 Mar 2013CPOL2 min read 88.4K   1.8K   51   24
How to create custom authorization policy and return HTTPContext Identity for authorization.

Introduction

Windows Communication Foundation provides tons of methods to authenticate users' check for authorization based on the service type that it's quite confusing to implement simple form based authentication and role based authorization for WCF REST 4.0.

Note: This article assumes that the WCF REST service is hosted with the ASP.NET application and shares the same web.config. Make sure that Form Authentication is enabled in the web.config file.

Background

There are many ways to authenticate and authorize a user in a WCF Service, but in this example, the authentication cookie will already be created by the login page and that will be used by subsequent requests made to the REST service for authorization.

Using the Code

A typical way to authorize a user for a specific role is to use the Principal Permission attribute. Something like this:

C#
WebGet(UriTemplate = "")]
[PrincipalPermission(SecurityAction.Demand, Role="Admin")]
public List<SampleItem> GetCollection(){} 

But even though after the user is authenticated using Membership provider and HTTPContext.Current.User.Identity and the context is available at service level, the principal permission attribute always throws a security exception.

The reason for that is the principal permission attribute checks for System.Threading.Thread.CurrentPrincipal.Identity and not for the HTTPContext Identity.

To solve this problem, we have to create a Custom Principal and Authorization Policy for the WCF Service. Then this policy will be hooked with the WCF REST Service using ServiceBehaviour.

Custom Principal

Here is the code for the custom principal:

C#
public class CustomPrincipal: IPrincipal
{
    private IIdentity _identity;
    public IIdentity Identity
    {
        get
        {
            return _identity;
        }
    }

    public CustomPrincipal(IIdentity identity)
    {
        _identity = identity;
    }

    public bool IsInRole(string role)
    {
        return Roles.IsUserInRole(role);
    } 
} 

Here the ASP.NET Membership Role provider is used to verify if the user is in a particular role or not. We can have our custom implementation that does not use the Membership provider.

Authorization Policy

Now create an Authorization policy that sets the Custom Principal to the evaluation context:

C#
public class AuthorizationPolicy : IAuthorizationPolicy
{
    string id = Guid.NewGuid().ToString();

    public string Id
    {
        get { return this.id; }
    }

    public System.IdentityModel.Claims.ClaimSet Issuer
    {
        get { return System.IdentityModel.Claims.ClaimSet.System; }
    }

    // this method gets called after the authentication stage
    public bool Evaluate(EvaluationContext evaluationContext, ref object state)
    {
        // get the authenticated client identity
        IIdentity client = HttpContext.Current.User.Identity; 
        
        // set the custom principal
        evaluationContext.Properties["Principal"] = new CustomPrincipal(client);

        return true;
    }              
}

If you look closely, the custom principal is created using the HTTPContext Identity that was created after the user is authenticated using membership provider and the authentication cookie is set after validating the user. Something like this:

C#
FormsAuthentication.SetAuthCookie(username, false);

Attach Authorization Policy to WCF

This can be done by creating a service behavior in the web.config file. But here, I have created a custom service behavior by implementing IServiceBehavior and attaching the authorization policy to it.

C#
[AttributeUsage(AttributeTargets.Class)]
public class SecurityBehaviorAttribute : Attribute, IServiceBehavior
{
    public void ApplyDispatchBehavior(ServiceDescription serviceDescription, 
    	System.ServiceModel.ServiceHostBase serviceHostBase)
    {
        List<IAuthorizationPolicy> policies = new List<IAuthorizationPolicy>();
        policies.Add(new AuthorizationPolicy());
        serviceHostBase.Authorization.ExternalAuthorizationPolicies = 
						policies.AsReadOnly();

        ServiceAuthorizationBehavior bh =
            serviceDescription.Behaviors.Find<ServiceAuthorizationBehavior>();
        if (bh != null)
        {
            bh.PrincipalPermissionMode = PrincipalPermissionMode.Custom;
        }
        else
            throw new NotSupportedException();
    }

    public void AddBindingParameters(ServiceDescription serviceDescription, 
    	System.ServiceModel.ServiceHostBase serviceHostBase, 
    	System.Collections.ObjectModel.Collection<ServiceEndpoint> endpoints, 
    	System.ServiceModel.Channels.BindingParameterCollection bindingParameters) { }

    public void Validate(ServiceDescription serviceDescription, 
    	System.ServiceModel.ServiceHostBase serviceHostBase) { }
}

Here, ServiceAuthorizationBehavior PrincipalPermissionMode is set to Custom and the Authorization policy is added to the servicehost.

Service Code

Make sure that the service behavior is added as an attribute to the service class.

C#
[ServiceContract]
[AspNetCompatibilityRequirements(RequirementsMode = 
	AspNetCompatibilityRequirementsMode.Allowed)]
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
    [SecurityBehavior]    
public class Service1  {
    [WebGet(UriTemplate = "")]
    [PrincipalPermission(SecurityAction.Demand, Role="Admin")]
    public List<SampleItem> GetCollection()
    {
        var value =  System.Web.HttpContext.Current.User.Identity.IsAuthenticated;
        return new List<SampleItem>() { new SampleItem() 
			{ Id = 1, StringValue = "Hello" } };
    }
} 

That's all. Now we can add the PrincipalPermission attribute to any web method and authorize the user for a specific role. We can also implement a custom PrincipalPermission attribute to control the granularity of authorization.

Note: We can also create an Authentication service to validate the user name password and create an authentication cookie after validation. Here, the assumption is that WCF REST is hosted with the Web application and therefore shares the context.

Let me know if there is any better way to achieve the same thing without providing user name and password at each request.

License

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


Written By
Architect
India India
I work as a freelance consultant and is passionate about taking challenges in latest technology.
I am a solution architect and trainer with 9+ years experience in designing, developing and maintaining enterprise wide application using latest technology like SharePoint 2010, MOSS 2007, Business Intelligence, SQL Server 2008, Reporting Service, Analysis Service and Integration service.

Comments and Discussions

 
QuestionHow to implement JWT on WCF REST API Pin
Nagen011-Aug-18 6:19
Nagen011-Aug-18 6:19 
QuestionThank you for your great article Pin
Member 1330524021-Aug-17 20:49
Member 1330524021-Aug-17 20:49 
GeneralMy vote of 2 Pin
Nirosh19-Jun-14 23:18
professionalNirosh19-Jun-14 23:18 
Questionsome difficulty implementing Pin
noncitizen2-Jun-14 4:04
noncitizen2-Jun-14 4:04 
AnswerRe: some difficulty implementing Pin
Anupama Agarwal2-Jun-14 5:34
Anupama Agarwal2-Jun-14 5:34 
GeneralRe: some difficulty implementing Pin
noncitizen3-Jun-14 4:41
noncitizen3-Jun-14 4:41 
AnswerRe: some difficulty implementing Pin
noncitizen3-Jun-14 4:32
noncitizen3-Jun-14 4:32 
GeneralRe: some difficulty implementing Pin
Anupama Agarwal3-Jun-14 18:37
Anupama Agarwal3-Jun-14 18:37 
GeneralMy vote of 1 Pin
dave_dv7-Oct-13 3:25
dave_dv7-Oct-13 3:25 
QuestionRequest for principal permission failed Pin
yahav_g15-Jul-13 23:51
yahav_g15-Jul-13 23:51 
QuestionThanks!! Pin
paulopez26-Apr-13 23:06
paulopez26-Apr-13 23:06 
GeneralMy vote of 5 Pin
Prasad Khandekar20-Mar-13 22:19
professionalPrasad Khandekar20-Mar-13 22:19 
QuestionVisual Studio 2012 Pin
Nejimon CR19-Mar-13 7:38
Nejimon CR19-Mar-13 7:38 
AnswerRe: Visual Studio 2012 Pin
Anupama Agarwal22-Mar-13 21:13
Anupama Agarwal22-Mar-13 21:13 
GeneralRe: Visual Studio 2012 Pin
Nejimon CR23-Mar-13 4:41
Nejimon CR23-Mar-13 4:41 
QuestionLogin as service Pin
shivendra.kush15-Feb-13 18:28
shivendra.kush15-Feb-13 18:28 
AnswerRe: Login as service Pin
Anupama Agarwal22-Mar-13 21:12
Anupama Agarwal22-Mar-13 21:12 
QuestionNice solution Pin
Abbath134924-Dec-12 7:00
Abbath134924-Dec-12 7:00 
AnswerRe: Nice solution Pin
Anupama Agarwal25-Dec-12 8:37
Anupama Agarwal25-Dec-12 8:37 
Questiongood solution! Pin
antares763-Dec-12 0:35
antares763-Dec-12 0:35 
GeneralMy vote of 5 Pin
sandippatil19-Sep-12 21:45
sandippatil19-Sep-12 21:45 
GeneralMy vote of 5 Pin
mrwh12-Apr-12 10:48
mrwh12-Apr-12 10:48 
GeneralSimple Authentication implementation Pin
Member 4049653-Jan-12 19:29
Member 4049653-Jan-12 19:29 
GeneralMy vote of 5 Pin
nilu200426-Dec-11 1:06
nilu200426-Dec-11 1:06 

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

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