WCF Web API: Role based access control with basic authentication

Written on August 24, 2011

Authenticating users with WCF Web API using basic authentication is a common use case.

Yet this does not always fit all needs -- sometimes you may want to give users access to resources based on the roles they belong to.

We want to gain users role based access to resources by attributing the resource class with a RolesAttribute:

[AttributeUsageAttribute(AttributeTargets.Class)]
public class RolesAttribute : Attribute {
    readonly string _roles;

    public RolesAttribute(string roles) {
        _roles = roles;
    }

    public string Roles {
        get { return _roles; }
    }
}

Thus, our contacts resource class could look like this:

[ServiceContract]
[Roles("Admins")]
public class ContactsResource {

    [WebGet(UriTemplate = "")]
    public HttpResponseMessage<List<Contact>> Get(HttpRequestMessage request) {
        return new HttpResponseMessage<List<Contact>>(
            new List<Contact>()
                {
                        new Contact()
                                {
                                        Id= Guid.NewGuid(),
                                            Name = "Alexander Zeitler"
                                  }
                    });
    }

}

Now we need to verify that the authenticated user is in the neccessary roles for our contacts resource.

This is done by the use of an HttpOperationHandler.

public class RoleRequestHandler : HttpOperationHandler<HttpRequestMessage, HttpRequestMessage> {
    readonly IRoleRepository _roleRepository;

    public RoleRequestHandler(IRoleRepository roleRepository)
        : base("request") {
        _roleRepository = roleRepository;
    }

    public override HttpRequestMessage OnHandle(HttpRequestMessage request) {

        var userRoles = _roleRepository.GetRolesForUser(Thread.CurrentPrincipal.Identity.Name);

        var operationContext = OperationContext.Current;
        var serviceType = operationContext.Host.Description.ServiceType;

        var attribute = (serviceType.GetCustomAttributes(typeof(RolesAttribute),false)).FirstOrDefault();
        if (null != attribute) {
            var rolesAttribute = attribute as RolesAttribute;
            var roles = rolesAttribute.Roles.Split(",".ToCharArray()).ToList();
            if (roles.Where(r => r.Any(u => userRoles.Contains(r))).Count() > 0) {
                return request;
            }
            else {
                throw new HttpResponseException(
                    new HttpResponseMessage(
                        HttpStatusCode.Unauthorized, 
                        "You're not in the required roles."));
            }
        }
        return request;
    }

}

First, we're retrieving the the roles the authenticated (through basic auth) user belongs to.

Then, the type of the requested resource is read from the currents operation context description.

After this we try to get the roles attribute from the resource class type and finally check if the users matches at least one of these roles defined in the attributed contacts resource.

If this is the case, the request is forwarded to the contacts resource.

Otherwise we throw an exception containing a 401 unauthorized status code and a comprehensive reason phrase.

To wire things up, we need to assign our request handler to our configuration:

configuration
.AddRequestHandlers(c => c.Add(
            new RoleRequestHandler(
                container.Resolve<IRoleRepository>())), 
                (s, o) => true);

Next step should be access control at method level - lets see if I can handle this ;-)

I'll keep you up to date...

DotNetKicks-DE Image