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.
Pingback: WCF and Identity in .NET 4.5: Client Certificate Authentication | www.leastprivilege.com
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
This must be in the hosting code.
In IIS hosting you need a service host factory for that.
by hosting code do you mean for example, the constructor of the Service??
No – i mean the code that creates the ServiceHost.
would that be in the client??
No.
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.
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
Pingback: WCF and Identity in .NET 4.5: External Authentication with WS-Trust | www.leastprivilege.com
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.
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
MVC does not use token handlers. You would write your validation code on the login page.
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??
No. You need to either run claims transformation on every request, or set a WIF authentication session cookie.
http://brockallen.com/2013/01/26/replacing-forms-authentication-with-wifs-session-authentication-module-sam-to-enable-claims-aware-identity/
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.
WCF does not use cookies. Brock’s code is for web apps.
then the WriteSessionTokenToCookie(sessionCookie) will be all to avoid the transformation logic on any request???
That’s caching the claims principal across round trips, yes.
Thanks a lot…
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
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
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?
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]
You have to register the config section. Google is your friend ;)
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.
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.
This is what I placed in the config file in my previous post but this is not working.
serviceAuthorization principalPermissionMode=“Always“
I have exactly almost same config settings but my validator is not being called.
I have tried all different approaches from the web but none working.
Any suggestions you can give me.
Thanks,
Thank You for the valuable information.
I managed to make my WCF WIF .NET 4.5 service to work with custom ASP.NET Identity 2 implementation.
My UserNameSecurityTokenHandler is called on service operations (it took me more time because I have forgot to override CanValidateToken to true).
The problem I still have is that my ClaimsAuthorizationManager using Thread.CurrentPrincipal does not work because the principal is generic nonauthenticated one even though OperationContext.Current.ClaimsPrincipal is the correct one and principalPermissionMode=“Always“ is set in my configuration.
IIRC – in the authZ manager you get the principal from the AuthorizationContext
Sure, I know.
But before it gets there, ClaimsPrincipalPermission is used in WCF method which gets it from Thread.CurrentPrincipal.
I have written my custom validation using OperationContext.ClaimsPrincipal directly.
Hello, Dominick Baier!
Could you share the full project with Identity authentication (WCF server side and WEB client side)? I have a problem and need an example.
I’ve built all logic on server side and now don’t know how to get owin context for client side and make other communication.
Sorry for my English if you don’t understand me.
Hi
sorry – I don’t have the code anymore.
This post is kinda old, but today we have to use WCF, in the organization we work with OAuth2 through Identity Server 4, I was wondering if would be possible to authenticate a Identity Server Client through this with clientCredentialType=”username”, I mean the external client send its clientId and secret and instead of verify the passwords are equals (such this dummy example) verify the client and secret.
Yep thats possible. But I don’t have any sample code right now.