Click here to Skip to main content
Click here to Skip to main content
Go to top

Custom Authorization in WCF

, 4 Mar 2009
Rate this:
Please Sign up or sign in to vote.
This article will show you how to implement custom authentication

Introduction

Windows Communication Foundation (WCF) provides powerful facilities for implementing authorization in services.

This article will show you how to implement custom authentication. Only user with ADMIN role can call the Login() method.

Note: This article only focuses on checking role of user. You can modify this code for checking username, password and role of user by reading from database.

SERVICE

Create Custom Principal

It is to supply your custom IPrincipal implementation to WCF. This gives you the chance to implicitly run code after the authentication stage of each request. For this, you have to create your own custom principal and return it to the WCF plumbing. The custom principal will then be available from Thread.Cur­rentPrincipal to the service code. Custom principals allow full customization of role-based security and expose specialized security logic for the service developer to use.

Writing a custom principal is straightforward. Simply implement the IPrincipal interface and add your own custom functionality. To integrate the principal with WCF, you have to set the Principal­Per­missionMode attribute in the ServiceAuthorization element to "custom" and provide an authorization policy that is responsible for creating the principal and giving it back to WCF. 

An IPrincipal-derived class must implement a single method called IsInRole where you can pass in a role name and get a Boolean response. In addition, the principal has a reference to the identity class it wraps. 

class CustomPrincipal : IPrincipal
{
public bool IsInRole(string role)
    {
        EnsureRoles();

        return _roles.Contains(role);
    }

    // read Role of user from database
    protected virtual void EnsureRoles()
    {
        if (_identity.Name == "AnhDV")
            _roles = new string[1] { "ADMIN" };
        else
            _roles = new string[1] { "USER" };
    }
}

Create Custom Authorization Policy 

An authorization policy is simply a class that implements the System.IdentityModel.Policy.IAuthorizationPolicy interface. Implementations of this interface must employ a method called Evaluate, which gets called on every request. Here you can reach into the WCF service security context and set the custom principal. The code below shows a custom principal as well as the authorization policy and its corresponding configuration entries.

The important part of AuthorizationPolicy:IAuthorizationPolicy is the Evaluate() method which, using the context of the claim evaluation, trys to get the PrimaryIdentity. This property represents the identity discovered by WCF during user credentials validation. The method then converts this to an CustomPrincipal and attaches it to the current context thus making the principal available on the current running thread. 

class AuthorizationPolicy : IAuthorizationPolicy
{
    Guid _id = Guid.NewGuid();

    // this method gets called after the authentication stage
    public bool Evaluate(EvaluationContext evaluationContext, ref object state)
    {
        // get the authenticated client identity
        IIdentity client = GetClientIdentity(evaluationContext);

        // set the custom principal
        evaluationContext.Properties["Principal"] = new CustomPrincipal(client);

        return true;
    }

    private IIdentity GetClientIdentity(EvaluationContext evaluationContext)
    {
        object obj;
        if (!evaluationContext.Properties.TryGetValue("Identities", out obj))
            throw new Exception("No Identity found");

        IList<IIdentity> identities = obj as IList<IIdentity>;
        if (identities == null || identities.Count <= 0)
            throw new Exception("No Identity found");

        return identities[0];
    }
} 
 <serviceAuthorization principalPermissionMode="Custom">
  <authorizationPolicies>
   <add policyType="WikiService.AuthorizationPolicy, App_Code/WikiSecurity" />
  </authorizationPolicies>
 </serviceAuthorization>  

Create Custom Validator

If left to itself, the service will now try and authenticate the supplied credentials against Windows user accounts and, as we are using our own custom authentication, this will fail!

The password stuff lives in the following override, I call my security module to check the user and password against the db:

public class CustomValidator : UserNamePasswordValidator
{
    public override void Validate(string userName, string password)
    {
        // validate arguments
        if (string.IsNullOrEmpty(userName))
            throw new ArgumentNullException("userName");
        if (string.IsNullOrEmpty(password))
            throw new ArgumentNullException("password");

        // check the user credentials from database
        //int userid = 0;
        //CheckUserNameAndPassword(userName, password, out userid);
        //if (0 == userid)
        //throw new SecurityTokenException("Unknown username or password");
    }
}

We indicate to the service that we want our own class, 'CustomValidator' which lives in the assembly 'WikiSecurity', to do the password stuff using this config:   

<userNameAuthentication userNamePasswordValidationMode="Custom"
   customUserNamePasswordValidatorType="CustomValidator, App_Code/WikiSecurity" />  

Create Certificate 

You are able to send your username and password over the wire if you use the following config. 

NOTE: You will need to create and install a suitable certificate on the server side to support this, another story... 

<security mode="Message">
<message clientCredentialType="UserName"/>
</security>  

You must open \Certificate Setup Application\Certificate Setup\Source Code\Certificate Setup.sln and run it to create your temp certificate example: g2-anhdv-xp.fsoft.fpt.vn

And modify your config: 

<serviceCertificate findValue="g2-anhdv-xp.fsoft.fpt.vn" 
	x509FindType="FindBySubjectName" />

Your Service Code: Example WikiSecurity Class

Only user with ADMIN role can call the Login() method of the WikiSecurity service: 

[PrincipalPermission(SecurityAction.Demand, Role = "ADMIN")]
public void Login()
{

} 

CLIENT

The username and password can then be set on your client proxy something like:

var factory = new ChannelFactory<IWikiSecurity>("*");
factory.Credentials.UserName.UserName = "AnhDV";//Do Viet Anh
factory.Credentials.UserName.Password = "anhdv";
var wikiProxy = factory.CreateChannel();
wikiProxy.Login();//-->Successful

var factory = new ChannelFactory<IWikiSecurity>("*");
factory.Credentials.UserName.UserName = "AnhDV1";//Do Viet Anh 1
factory.Credentials.UserName.Password = "anhdv";
var wikiProxy = factory.CreateChannel();
wikiProxy.Login();//Access Denied

Hope this helps.

Enjoy and have fun.

History

  • 4th March, 2009: Initial post

License

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

Share

About the Author

vietanh154
Software Developer (Senior) FPT Software JSC & VietSoftware International
Vietnam Vietnam
Programming is my hobby!
Vietanh154, vietanh154@yahoo.com or vietanh154gmail.com
Manager for Concat Group: http://nhómConcatGroup.vn/
Project Manager & Technical Leader for FPT Software JSC
Solution Architect & Technical Manager for VietSoftware International
 
Certification:
Microsoft Certified Application Developer For Microsoft .NET
Microsoft Certified Professional MCP 2.0 -- Certified Professional
Developing and Implementing Web Applications with Microsoft® Visual C#™ .NET and Microsoft® Visual Studio® .NET
Developing and Implementing Windows®-based Applications
with Microsoft® Visual C#™ .NET and Microsoft® Visual Studio® .NET
Developing XML Web Services and Server Components with
Microsoft Visual C# .NET and the Microsoft .NET Framework

Comments and Discussions

 
GeneralSetting the credentials using the PinmemberFederico Colombo24-May-11 12:31 
GeneralRe: Setting the credentials using the Pinmembervietanh15423-Jul-12 22:02 
QuestionHow to create Custom Identity object? PinmemberPradeep Babu Yadagani9-Feb-11 15:48 
AnswerRe: How to create Custom Identity object? Pinmembervietanh15423-Jul-12 21:59 
GeneralExcellent and easy to understand, but one question... Pinmembercihlen25-Jan-11 9:00 
GeneralRe: Excellent and easy to understand, but one question... Pinmembervietanh15423-Jul-12 21:49 
GeneralMy vote of 5 Pinmemberjitendraswm12-Jan-11 23:13 
GeneralRe: My vote of 5 Pinmembervietanh15423-Jul-12 21:47 
GeneralCould not load type 'AuthorizationPolicy' from assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' Pinmemberdharric7-Jan-11 17:24 
GeneralRe: Could not load type 'AuthorizationPolicy' from assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' Pinmemberfaraujopacheco20-Jul-12 18:19 

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.140916.1 | Last Updated 4 Mar 2009
Article Copyright 2009 by vietanh154
Everything else Copyright © CodeProject, 1999-2014
Terms of Service
Layout: fixed | fluid