18 March, 2011

How to: WCF and custom Authentication (username/password)

This post details how you can customize the way users of your WCF-services are authenticated. But, before engaging in this procedure, let’s first establish some terminology/concepts:
Authentication: to establish the identity of an individual/entity.
Authorization: to determine whether an authenticated (identified) individual is allowed access to a system. You Authorize access to a system.
This post is about Authentication (identity establishment)
Authentication is a subject that is pretty hard to deal with yourself. A number of proven and established mechanisms already are in place solving this functionality out-of-the-box in WCF. Due to the very nature of an identity token (it carries the “identity” of an individual), it is shielded with proper usage of cryptography. Therefore – it is very hard to do much in this area yourself. It a completely different story when it comes to Authorization (another post).
Where does Authentication fit into the overall picture? As evident from the figure – the Authentication is the first action taken when an incoming request is received. It makes actually a lot of sense, as it is not possible to perform Authorization before you have established the identity (Authentication) of the incoming user/system.
image
In native WCF - the following security token types (credential types) are supported:
    • Username Token (points by default to an ASP.NET database)
    • X.509 Certificate Token (digital certificates)
    • Kerberos Token (Windows Active Directory)
    • SAML Token (generic Security Assertion Markup Language; also signed with certificate)
As seen, a number of implementations are already in place. So - when would you have a need for this proposed customization? The only one I can think of is, if you are not using e.g. the standard ASP.NET Membership database, but you have rolled your own user storage. In such scenario, it might make sense to use this customization. And the customization in play here would most often be: username/password authentication.
 How do you establish your own Username/Password Authentication?
The entire solution is seen here. It is a Client console, Server Console and a shared library with Contracts (ICalculatorService). Sharing a contract between the two makes sense when you have control of both Client and Server as is the case in this example.
image
The various parts are seen below.
Shared ContractThe shared contract is simply a type safe way of having both Client/Server knowing the types and methods used.
[ServiceContract]
public interface ICalculatorService{
    [OperationContract]
    int Add(int a, int b);

    [OperationContract]
    int Subtract(int a, int b);
}



ServerThe server side is the place where the “meat” is. But, as WCF is constructed in a very clever “stack-like” manner, security is deliberately kept out of the business logic (of the Calculator in this case!). Therefore, you do not see anything security related in the below service implementation. The security is kicking in long time before in the stack before the call ultimately reaches the below business-logic (see the illustration above).
public class CalculatorService : ICalculatorService {
    public int Add(int a, int b)
    {
        return a + b;
    }

    public int Subtract(int a, int b)
    {
        return a - b;
    }
}

The security (and in this case Authentication-part) is created as a custom override of UserNamePasswordValidator. If the incoming credentials do not match user=”Ole” and password=”Pwd”, an AuthenticationException is thrown. These values are of-course to be looked up in your own user storage (a database or…?) Should the credentials however match an identity in your store, no exceptions are thrown.
/// <summary>
/// Custom impl. of u/p validation./// </summary>class UsernameAuthentication : UserNamePasswordValidator {
    /// <summary>
    /// When overridden in a derived class, validates the specified username and password.
    /// </summary>
    /// <param name="userName">The username to validate.</param><param name="password">The password to validate.</param>
    public override void Validate(string userName, string password)
    {
        //TODOs: Lookup match in user storage (DB?).
        var ok = (userName == "Ole") && (password == "Pwd"); 
        if(ok == false)
            throw new AuthenticationException("u/p does not match");
    }
}

Important: Now – you might ask yourself - where is the identity matching the provided credentials actually being set? After all, this is what we are after here; to convert a set of credentials => an identity? As long as we are inheriting from the UserNamePasswordValidator, this is taken care of for us behind the scenes. The UserNamePasswordValidator will create a NameClaim in the authentication chain taking the username as “name”. The NameClaim (= username) is considered an Identity in this scenario.

Server Configuration
To make your service use this custom validation stuff, you need to tweak some settings in <app>.config for the service. Especially, the <usernameAuthentication> settting needs to have a validationMode = “Custom”.
<configuration>
  <system.serviceModel>
    
    <services>
      <service name="Server.CalculatorService" behaviorConfiguration="customCred">
        <endpoint address="net.tcp://localhost:9090/CalcSvc"
                  binding="netTcpBinding"
                  bindingConfiguration="secUP"
                  contract="SharedContracts.ICalculatorService"/>
      </service>
    </services>
    
    <bindings>
      <netTcpBinding>
        <binding name="secUP">
          <security mode="Message">
            <message clientCredentialType="UserName"/>
          </security>
        </binding>
      </netTcpBinding>
    </bindings>
    
    <behaviors>
      <serviceBehaviors>
        <behavior name="customCred">
          <serviceCredentials>
            <!--Service identity + encryption certificate-->
            <serviceCertificate findValue="localhost" x509FindType="FindBySubjectName" 
                                storeLocation="LocalMachine" storeName="My" />
            
            <userNameAuthentication userNamePasswordValidationMode="Custom"
              customUserNamePasswordValidatorType="Server.Auth.UsernameAuthentication, Server" />
          </serviceCredentials>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

As seen, a (reasonable) simple configuration is required.



ClientThe clients needs to provide some credentials to the service (we just set this up!).
static void Main(string[] args)
{
    Console.WriteLine("Client - starting...");
    Console.WriteLine("Press key to start...");

    Console.Read();

    var fac = new ChannelFactory<ICalculatorService>("tcpEP");
    //set credentials
    fac.Credentials.UserName.UserName = "Ole"; 
    fac.Credentials.UserName.Password = "Pwd";

    var prox = fac.CreateChannel();
    using (prox as IDisposable)
    {
        var res = prox.Add(1, 2);
        Console.WriteLine("Result: {0}", res);
    }

    Console.Read();
}

That’s about what it takes to create a custom username/password validation against your own data store. Happy coding, guys!

Update: Download project here
http://www.clauskonrad.net/download.ashx?id=15


Technorati Tags: ,

11 comments:

Anonymous said...

Not great. Want a generic design without hard coding

Anonymous said...

Can you provide downloadable solution project?

Claus Konrad said...

There is no hardcoding here?
If you are refering to the user/pwd, then it's just for the sake of demonstration purpose.

Claus Konrad said...

Updated with downloadable project.

Anonymous said...

Maybe want to take it out of source control so the project loads for others.

Baz L said...

Questions:

1. Does the client need to supply this information for *every* request?
2. Is there some way to authenticate once, get some sort of token and use this for subsequent calls?

Claus Konrad said...

1: The credentials are attributed to the proxy you create in the client runtime (using the factory). Every time you ask a factory to create a proxy - you need to set these values to gain a valid proxy. Otherwise communication etablishment will fail.

2: As long as you use the same proxy instance - you can call as many times as you want. No need for re-authenticate; the WCF-plumbing takes care of this.

Display name said...

I want to download your source code for study, But cannot.

Thichca2 said...

Can I provide custom authenticate in addition to standard Windows Authentication.
I have a service that I want to ensure that only my client library can access this service but no one else.
application can use my client library to access the service by providing Windows user account but internally my library also is authenticated with the service.
Thanks

Claus Konrad said...

Have a look at this:

http://blog.clauskonrad.net/2010/04/wcf-restrict-which-clients-can-call.html

for IT the said...

Authentication identity establishment WCF Traininguseful article

Dot Net Training in Chennai |

InRiver: Not loading your extensions?

(You really need to in the loop to appreciate the issue this post addresses). Man, I've been fighting this problem for hours before I ...