Click here to Skip to main content
15,881,898 members
Articles / Programming Languages / C#

Custom Authorization in WCF

Rate me:
Please Sign up or sign in to vote.
4.86/5 (26 votes)
4 Mar 2009CPOL3 min read 195.4K   7K   79   38
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. 

C#
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. 

C#
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];
    }
} 
XML
 <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:

C#
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:   

XML
<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... 

XML
<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: 

XML
<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: 

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

} 

CLIENT

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

C#
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)


Written By
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

 
QuestionAppreciation-Was an excellent Article Pin
Member 1104727024-May-17 3:24
Member 1104727024-May-17 3:24 
QuestionThere are some problems. Pin
f0rb1dd3n40326-Apr-13 2:47
f0rb1dd3n40326-Apr-13 2:47 
AnswerRe: There are some problems. Pin
f0rb1dd3n40327-Apr-13 2:33
f0rb1dd3n40327-Apr-13 2:33 
GeneralRe: There are some problems. Pin
Roathvb23-May-14 4:36
Roathvb23-May-14 4:36 
GeneralRe: There are some problems. Pin
Roathvb23-May-14 5:35
Roathvb23-May-14 5:35 
QuestionFirst Of All Thanks a lot, I have a question Pin
mahdi87_gh14-Oct-12 20:41
mahdi87_gh14-Oct-12 20:41 
GeneralMy vote of 5 Pin
mahdi87_gh14-Oct-12 20:38
mahdi87_gh14-Oct-12 20:38 
QuestionModification to the existing code - for Login() Method Pin
Kishore Babu. Peddi19-Dec-11 14:08
Kishore Babu. Peddi19-Dec-11 14:08 
AnswerRe: Modification to the existing code - for Login() Method Pin
vietanh15420-Dec-11 4:10
vietanh15420-Dec-11 4:10 
QuestionRe: Modification to the existing code - for Login() Method Pin
Kishore Babu. Peddi20-Dec-11 8:45
Kishore Babu. Peddi20-Dec-11 8:45 
AnswerRe: Modification to the existing code - for Login() Method Pin
vietanh15423-Jul-12 21:38
vietanh15423-Jul-12 21:38 
GeneralSetting the credentials using the Pin
Federico Colombo24-May-11 12:31
Federico Colombo24-May-11 12:31 
GeneralRe: Setting the credentials using the Pin
vietanh15423-Jul-12 22:02
vietanh15423-Jul-12 22:02 
QuestionHow to create Custom Identity object? Pin
Pradeep Babu Yadagani9-Feb-11 15:48
Pradeep Babu Yadagani9-Feb-11 15:48 
AnswerRe: How to create Custom Identity object? Pin
vietanh15423-Jul-12 21:59
vietanh15423-Jul-12 21:59 
GeneralExcellent and easy to understand, but one question... Pin
hcihlen25-Jan-11 9:00
hcihlen25-Jan-11 9:00 
GeneralRe: Excellent and easy to understand, but one question... Pin
vietanh15423-Jul-12 21:49
vietanh15423-Jul-12 21:49 
GeneralMy vote of 5 Pin
jitendraswm12-Jan-11 23:13
jitendraswm12-Jan-11 23:13 
GeneralRe: My vote of 5 Pin
vietanh15423-Jul-12 21:47
vietanh15423-Jul-12 21:47 
GeneralCould not load type 'AuthorizationPolicy' from assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' Pin
dharric7-Jan-11 17:24
dharric7-Jan-11 17:24 
GeneralRe: Could not load type 'AuthorizationPolicy' from assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' Pin
faraujopacheco20-Jul-12 18:19
faraujopacheco20-Jul-12 18:19 
GeneralRe: Could not load type 'AuthorizationPolicy' from assembly 'System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089' Pin
vietanh15423-Jul-12 21:46
vietanh15423-Jul-12 21:46 
GeneralGreat Article and exactly what I'm looking for but.... Pin
Nick2105772-Mar-10 6:17
Nick2105772-Mar-10 6:17 
GeneralRe: Great Article and exactly what I'm looking for but.... Pin
vietanh15411-Jul-10 16:50
vietanh15411-Jul-10 16:50 
GeneralValidate function is not invoked. Pin
QuikGuide3-Feb-10 19:46
QuikGuide3-Feb-10 19:46 
The article is nice. Except I am facing problem that my Validate function is not invoked. and my service picking up default system username instead of the credentials I have provided.

I removed certificate also. Is it mandatory to config certificate.

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.