WCF and Identity in .NET 4.5: UserName/Password Authentication

overview scenarios accessing claims windows authentication

I use this configuration:

<system.serviceModel>

    <services>

      <service name=Common.ClaimsService>

        <endpoint address=usernamemixed

                  binding=netHttpBinding

                  contract=Common.IClaimsService />

      </service>

    </services>

 

    <bindings>

      <netHttpBinding>

        <binding>

          <security mode=TransportWithMessageCredential>

            <message clientCredentialType=UserName />

          </security>

        </binding>

      </netHttpBinding>

    </bindings>

 

  </system.serviceModel>

 

In the client, you now have to explicitly pass in a username and password:

var factory = new ChannelFactory<IClaimsService>(“*”);

factory.Credentials.UserName.UserName = “username”;

factory.Credentials.UserName.Password = “password”;

 

var proxy = factory.CreateChannel();

var id = proxy.GetIdentity();

 

By default WCF will check the credentials against the Windows account database, and if authentication is successful, you get a full fledged WindowsPrincipal/WindowsIdentity in your service (just as with Windows integrated authentication).

Custom Authentication
When you want to authenticate against a custom credential store, you need to configure a custom password validator that in the <serviceCredentials /> behavior:

<behaviors>

  <serviceBehaviors>

    <behavior>

 

      <serviceCredentials>

        <userNameAuthentication userNamePasswordValidationMode=Custom

                                customUserNamePasswordValidatorType=Type, Assembly />

      </serviceCredentials>

    </behavior>

  </serviceBehaviors>

</behaviors>

 

A sample implementation looks like this:

public class PasswordValidator : UserNamePasswordValidator

{

    public override void Validate(string userName, string password)

    {

        if (userName != password)

        {

            throw new SecurityTokenValidationException();

        }

    }

}

 

Now when you run that, you’ll notice that you get an anonymous WindowsPrincipal on Thread.CurrentPrincipal. And the only way to get to the client’s username is via the ServiceSecurityContext. So this “old” approach is not really compatible with the claims world.

Enabling “WIF” Mode
In WIF mode you have much better claims integration, but there are also some changes you need to do to your authentication code. But first of all, enable WIF mode:

<serviceBehaviors>

  <behavior>

    <serviceCredentials useIdentityConfiguration=true />

  </behavior>

</serviceBehaviors>

 

The standard token handler again authenticates against Windows accounts. When you want to authenticate against your custom credential store, you need to supply a custom security token handler. This is not as simple as writing a password validator like the one above, but fortunately I have a really simpler wrapper in our Thinktecture.IdentityModel library.

You could configure your token handler also in configuration, but you can also do it via code:

host.Credentials.UseIdentityConfiguration = true;

 

var idConfig = new IdentityConfiguration();

idConfig.SecurityTokenHandlers.AddOrReplace(

    new GenericUserNameSecurityTokenHandler((uname, password) =>
      uname == password));

host.Credentials.IdentityConfiguration = idConfig;

 

…or if you simply want to wrap an existing password validator:

idConfig.SecurityTokenHandlers.AddOrReplace(

    new GenericUserNameSecurityTokenHandler(

        (uname, password) =>

            {

                try

                {

                    var validator = new PasswordValidator();

                    validator.Validate(uname, password);

                    return true;

                }

                catch (SecurityTokenValidationException)

                {

                    return false;

                }

            }));

 

The final step is to tell WCF to put the ClaimsPrincipal coming from the token handler on Thread.CurrentPrincipal using the following service behavior:

<serviceAuthorization principalPermissionMode=Always />

The end result is a ClaimsPrincipal containing the username, authentication method and authentication instant claims. Also the claims transformation/validation/authorization pipeline will be called if configured.

This entry was posted in IdentityModel, WCF. Bookmark the permalink.

30 Responses to WCF and Identity in .NET 4.5: UserName/Password Authentication

  1. Pingback: WCF and Identity in .NET 4.5: Client Certificate Authentication | www.leastprivilege.com

  2. Eric says:

    Hi Dominick, thanks for this post.
    Only one doubt, where should I place this code:
    idConfig.SecurityTokenHandlers.AddOrReplace(
    new GenericUserNameSecurityTokenHandler(
    (uname, password) =>
    {
    try
    {
    var validator = new PasswordValidator();
    validator.Validate(uname, password);
    return true;
    }
    catch (SecurityTokenValidationException)
    {
    return false;
    }
    }));

    thanks in advance,
    Eric

  3. Eric says:

    by hosting code do you mean for example, the constructor of the Service??

  4. No – i mean the code that creates the ServiceHost.

    • Eric says:

      would that be in the client??

      • Eric says:

        One last thing, and sorry for bothering.
        I’ve got a WPF client, I need to authenticate it against a WCF service, and my custom user-password validator gets invoked with all the required validation logic, but then I don’t want that process to get triggered per each request against the service. I saw your videos about identity and access control, in fact, I did it several times, and saw the idea of caching the claims using the FederatedAuthentication.SessionAuthenticationModule.WriteSessionTokenToCookie but that dosen’t work when the client is a WPF one. I don’t have a STS so my theorical idea of “what to do” is once authenticated the client, I create a token in the service that serves as the identifier that gets cached and is used in the client-server communication with a client claims-based identity using all the pipeline(ClaimsAuthenticationManager, etc). I just can’t find how since WriteSessionTokenToCookie seems to be fitting just asp.net clients needs. So a nearly have the what but no idea of how, and would appreciate so much any help.
        Thanks again,
        Eric.

  5. Well – a similar concept to the session cookie in WCF is called WS-SecureConversation. This can be enabled on certain bindings – but is not recommended since this creates a session with server affinity.

    Unfortunately it is not as straightforward in WCF as it is in ASP.NET/Web API.

    Either you cache as much as possible in your validator logic. Or you have to roll your own mechanism like i did here:

    https://github.com/thinktecture/Thinktecture.IdentityModel.45/tree/master/Samples/Web%20API/Clients/SessionTokenClient

  6. Pingback: WCF and Identity in .NET 4.5: External Authentication with WS-Trust | www.leastprivilege.com

  7. Landuber says:

    Hi Dominick,

    I am trying to do the same thing as above in IIS hosted app. What are the required components needed in web.config after using a custom security token handler?

    I tried

    but it is not getting invoked.

    Greatly appreciate your help on this.

    • There should be no difference between IIS hosted and self hosted from a WCF point of view. You certainly need to enable anonymous access in IIS since you do the password handling inside your service.

  8. Eric says:

    Hi Dominick,
    In WCF I have the ServiceHostFactory which I can use to add the custom token handler( GenericUserNameSecurityTokenHandler is great!!!), but, know I need to use it in and ASP.NET MVC4 app and there is no ServiceHostFactory because I have no WCF layer. So, how can I specify the custom token handler and write the code where I pass in the logic of the custom validation???
    thanks in advance

  9. MVC does not use token handlers. You would write your validation code on the login page.

    • Eric says:

      ok, but, after successfully logged in (maybe not using Membership), is the ClaimsAuthenticationManager’s derived class enough to set the ClaimsPrincipal once and for all, using the mechanism you showed in the Identity course, and by just declaring the class holding the transformation logic in the Web.config??

    • John Waters says:

      Dominick, I am using this approach to set the ClaimsPrincipal, and I also added in Brocks code to set a WIF authentication session cookie. But the username password validator still gets called on every request. Could it be that the WCF client isn’t passing the cookie back? It is a WinForms app that uses a generated service proxy.

  10. Eric says:

    then the WriteSessionTokenToCookie(sessionCookie) will be all to avoid the transformation logic on any request???

  11. That’s caching the claims principal across round trips, yes.

  12. Eric says:

    Thanks a lot…

  13. Richard says:

    Hi Dominick,
    I have just finished your PluralSight courses on 4.5 claims and am trying to get username security to work with my service. Currently, I’m just developing on the one local machine. Client and Host talk fine until I (try to) enable security. I get the error:
    An error occurred while making the HTTP request to https://localhost:8735/slsync. This could be due to the fact that the server certificate is not configured properly with HTTP.SYS in the HTTPS case. This could also be caused by a mismatch of the security binding between the client and the server.

    Do I need to create a SSL cert and configure port 8735 to use it? You didn’t mention this in the course but maybe it’s assumed knowledge? Or is this just a config matter? I’m a bit lost here…

    btw, great course, thanks.

    • Yes you need to create an SSL cert and associate it with the port. I assumed that knowledge ;)

      For the cert you can use makecert.exe and for the port mapping netsh.exe

      • goroth says:

        I just watched the pluralsight video and was unable to get the project to work because of incorrect cert setup. Here are the steps I used to fix the problem.

        How to add the IIS Express Development Certificate to other ports
        1.) Open MMC
        2.) Add Certificates / Computer Account
        3.) Expand Certificates –> Personal –> Certificates
        4.) Find the cert called localhost with friendly name “IIS Express Development Certificate”
        5.) Double click the localhost cert
        6.) Click on the Details tab inside the cert
        7.) Scroll down the list of details and click on the Thumbprint field
        8.) Copy the Thumbprint key into notepad and then remove all the spaces. This will become the certhash for installing the cert to a port.
        9.) Open the Visual Studio command prompt. This is normally found on the start menu under Visual Studio.
        10.) Type the following command. Change only the port number but not the 0.0.0.0 and change the certhash to match the Thumbprint.
        netsh http add sslcert ipport=0.0.0.0:444 certhash=6a9de14160b19f272267eb3482913b1a149c5a77 appid={00000000-0000-0000-0000-000000000000}
        11.) Go back to the localhost cert and right click on the cert and hit copy
        6.) Expand Certificates –> Trusted Root Certification Authorities –> Certificates
        7.) Right click on the Certificates folder under Trusted Root Certification Authorities and then click paste

  14. Richard says:

    Thanks Dominick. I’m getting there :)

    SSL is sorted. Now I get the error:
    Unrecognized configuration section system.identityModel

    In my config I have:

    What am I missing?

  15. Richard says:

    Oh. Your blog doesn’t like the angle brackets. I’ve swapped them for square ones…
    [system.identityModel]
    [identityConfiguration]
    [securityTokenHandlers]
    [remove type="System.IdentityModel.Tokens.WindowsUserNameSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/]
    [add type="Host.CustomUserNameSecurityTokenHandler, Host"/]
    [/securityTokenHandlers]
    [/identityConfiguration]
    [/system.identityModel]

  16. You have to register the config section. Google is your friend ;)

  17. Richard says:

    Thanks Dominick.
    In case it helps others, here’s the answers to my road-blocks.
    1. To use transport security, you need a ssl cert and it needs to be associated with the port you are using. Here are the commands I used for this:
    makecert -r -pe -n CN=”localhost” -eku 1.3.6.1.5.5.7.3.1 -ss my -sr localmachine -sky exchange -sp “Microsoft RSA SChannel Cryptographic Provider” -sy 12
    then
    netsh http add sslcert ipport=0.0.0.0:8735 certhash=81b4cea0ce214d3b8a6f63364ad04008547c1717 appid={00112233-4455-6677-8899-AABBCCDDEEFF}
    where certhash is the thumbprint of the cert and the appid is any guid.
    You also need to copy the certificate from your localmachine personal certificates to your localmachine TrustedRootCertificationAuthorities. Otherwise, wcf will reject the connection.
    Hanselman’s blog helps: http://www.hanselman.com/blog/WorkingWithSSLAtDevelopmentTimeIsEasierWithIISExpress.aspx
    2. You need to register the system.identitymodel config section at the top of your app.config with:
    [configSections]
    [section name="system.identityModel" type="System.IdentityModel.Configuration.SystemIdentityModelSection, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/]
    [section name="system.identityModel.services" type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089"/]
    [/configSections]
    (angle brackets replaced with square brackets so it will post here)
    3. The easy way to register GenericUserNameSecurityTokenHandler is in your app.config and put the handler in your wcf library because then it will work in your local host and your iis host exactly the same. It looks like this:
    [system.identityModel]
    [identityConfiguration]
    [claimsAuthenticationManager type="SLSyncService.ClaimsTransformer, SLSyncService"/]
    [securityTokenHandlers]
    [remove type="System.IdentityModel.Tokens.WindowsUserNameSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/]
    [add type="SLSyncService.CustomUserNameSecurityTokenHandler, SLSyncService"/]
    [/securityTokenHandlers]
    [/identityConfiguration]
    [/system.identityModel]
    (there’s a claims transformer there to incase that helps).
    4. To host the library in iis is super simple. Use something like this in your service.svc:
    [%@ ServiceHost Language="C#" Debug="true" Service="SLSyncService.PatientService" %]
    BUT…
    5. After enabling https in iisexpress, I just couldn’t get the https version to work! Eventually (!) I worked out it was a bug in iisexpress 8 and reinstalling it from the control panel fixed it. Grr.
    6. The service reference changes when you host in iis – it no longer uses the base address in your config but the service.svc address. Took me a while to get this one (I guess I’m a bit slow). If you regenerate your service reference you’ll get it though.
    7. Then, after doing all this I wanted to put it on the new azure websites… I just published and it worked. I fell out of my chair – not one gotcha!
    Anyways, it took me a few days to learn this. Hopefully reporting a little of my journey may help someone else.

  18. goroth says:

    So this code listed in the config …

    The end result is a ClaimsPrincipal containing the username, authentication method and authentication instant claims. Also the claims transformation/validation/authorization pipeline will be called if configured.


    Did NOT set the ClaimsPrincipal.Current.Identity or Thread.CurentPrincipal.Identity to the user passed in the service. They both still have IsAuthenticated = false and name = “”

    Any idea what is wrong? The CreatIdentityConfiguration is getting called and so is the PasswordValidator method.

    • goroth says:

      This is what I placed in the config file in my previous post but this is not working.
      serviceAuthorization principalPermissionMode=“Always“

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s