WCF UserName authentication

HI,

I'm trying to implement username authentication for a WCF service (hosted in ServiceHost, not IIS) and once service starts it gets to Faulted state if i specify:

tcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;

Here's the piece of code where service is being started (all settings, e.g. endpoints, behaviors are set in code - there is no app.config in the project):


//////-------------------------------
urlService =
"http://localhost:8000/MyService";

host = new ServiceHost(typeof(ServiceLibrary.service1));
host.Opening += new EventHandler(host_Opening);
host.Opened += new EventHandler(host_Opened);
host.Closing += new EventHandler(host_Closing);
host.Closed += new EventHandler(host_Closed);
host.Faulted += new EventHandler(host_Faulted);

WSHttpBinding tcpBinding = new WSHttpBinding();
tcpBinding.TransactionFlow = false;
tcpBinding.Security.Mode = SecurityMode.Message;
tcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;

host.AddServiceEndpoint(typeof(ServiceLibrary.IService1), tcpBinding, urlService);

ServiceMetadataBehavior metadataBehavior;
metadataBehavior = host.Description.Behaviors.Find<ServiceMetadataBehavior>();
if (metadataBehavior == null)
{
metadataBehavior = new ServiceMetadataBehavior();
metadataBehavior.HttpGetUrl = new Uri(
"http://localhost:8000/MyService");
metadataBehavior.HttpGetEnabled = true;
host.Description.Behaviors.Add(metadataBehavior);
}
ServiceCredentials serviceCred = host.Description.Behaviors.Find<ServiceCredentials>();
if (serviceCred == null)
{
serviceCred = new ServiceCredentials();

serviceCred.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;

serviceCred.UserNameAuthentication.CustomUserNamePasswordValidator = new MyCustomUserNameValidator();
host.Description.Behaviors.Add(serviceCred);
}
host.Open();

//-------------------------------------------------------------------

If i don't put "tcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;" service starts and works (if i exclude authentication) but of course authentication doesn't work.


Any ideas what i might do wrong

Thank you,
Andrey


Answer this question

WCF UserName authentication

  • Apollo13

    I use the username token as supporting token. The SDK has the supporting token and this i achieve using a custom binding. Please note that the custom binding is kind of a listing down the parts of a binding in detail. Every standard binding can be represented as a custom binding.



  • Chardiot

    Hi Sajay,

    I ran the code from the blog article you mentioned and it works fine on the same computer without need to install any certs. Do you know if it would work as fine if client ans service are on different computers and in different domains across internet Also, is it a shortcut or a normal practice

    Thanks a lot for your help!

    Andrey


  • Mike Lapierre

    Hi Sajay,

    Seems like i found the way - i just don't know if it's a rigth approach :) - i loop through all supporting tokens and get the one of type UserNameSecurityToken:

    public string CurrentUser()

    {

    String sUser = "";

    IEnumerator<SupportingTokenSpecification> i = OperationContext.Current.SupportingTokens.GetEnumerator();

    i.Reset();

    while (i.MoveNext())

    {

    SupportingTokenSpecification sts = i.Current;

    UserNameSecurityToken st = sts.SecurityToken as UserNameSecurityToken;

    if (st == null) continue;

    sUser = st.UserName;

    }

    return sUser;

    }

    Is it a right approach

    Thank you,

    Andrey


  • Peter Feigl

    Hi Govind,
    Thanks for your reply! The whole log was too big so here are some meaningful pieces of it:

    <ExceptionString>System.InvalidOperationException: The service certificate is not provided. Specify a service certificate in ServiceCredentials. </ExceptionString>
    at System.ServiceModel.ServiceHostBase.OnOpen(TimeSpan timeout)
    at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
    at System.ServiceModel.Security.SecuritySessionSecurityTokenAuthenticator.OnOpen(TimeSpan timeout)
    at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
    at System.ServiceModel.Channels.CommunicationObject.Open(TimeSpan timeout)
    at System.ServiceModel.Security.CommunicationObjectSecurityTokenAuthenticator.Open(TimeSpan timeout)
    at System.ServiceModel.Security.SecurityUtils.OpenCommunicationObject(ICommunicationObject obj, TimeSpan timeout)
    ... Further Removed ...
    <ExceptionString>System.InvalidOperationException: The ChannelDispatcher at 'http://localhost:8000/MyService' with contract(s) '"IssueAndRenewSession"' is unable to open its IChannelListener. ---&amp;gt; System.InvalidOperationException: The service certificate is not provided. Specify a service certificate in ServiceCredentials.
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateServerX509TokenProvider()
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateLocalSecurityTokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenProvider(SecurityTokenRequirement requirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateTlsnegoServerX509TokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateTlsnegoSecurityTokenAuthenticator(RecipientServiceModelSecurityTokenRequirement recipientRequirement, Boolean requireClientCertificate, SecurityTokenResolver&amp;amp; sctResolver)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, SecurityTokenResolver&amp;amp; outOfBandTokenResolver)
    at System.ServiceModel.Security.SecuritySessionSecurityTokenAuthenticator.SessionRenewSecurityTokenManager.CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, SecurityTokenResolver&amp;amp; outOfBandTokenResolver)
    <InnerException><Message>The service certificate is not provided. Specify a service certificate in ServiceCredentials. </Message><StackTrace>
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateServerX509TokenProvider()
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateLocalSecurityTokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenProvider(SecurityTokenRequirement requirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateTlsnegoServerX509TokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateTlsnegoSecurityTokenAuthenticator(RecipientServiceModelSecurityTokenRequirement recipientRequirement, Boolean requireClientCertificate, SecurityTokenResolver&amp;amp; sctResolver)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, SecurityTokenResolver&amp;amp; outOfBandTokenResolver)
    at System.ServiceModel.Security.SecuritySessionSecurityTokenAuthenticator.SessionRenewSecurityTokenManager.CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, SecurityTokenResolver&amp;amp; outOfBandTokenResolver)
    <ExceptionString>System.InvalidOperationException: The ChannelDispatcher at 'http://localhost:8000/MyService' with contract(s) '"IService1"' is unable to open its IChannelListener. ---&amp;gt; System.InvalidOperationException: The ChannelDispatcher at 'http://localhost:8000/MyService' with contract(s) '"IssueAndRenewSession"' is unable to open its IChannelListener. ---&amp;gt; System.InvalidOperationException: The service certificate is not provided. Specify a service certificate in ServiceCredentials.
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateServerX509TokenProvider()
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateLocalSecurityTokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenProvider(SecurityTokenRequirement requirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateTlsnegoServerX509TokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateTlsnegoSecurityTokenAuthenticator(RecipientServiceModelSecurityTokenRequirement recipientRequirement, Boolean requireClientCertificate, SecurityTokenResolver&amp;amp; sctResolver)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, SecurityTokenResolver&amp;amp; outOfBandTokenResolver)
    --- End of inner exception stack trace ---</ExceptionString><InnerException><ExceptionType>System.InvalidOperationException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>The ChannelDispatcher at 'http://localhost:8000/MyService' with contract(s) '"IssueAndRenewSession"' is unable to open its IChannelListener.</Message><StackTrace> at System.ServiceModel.Dispatcher.ChannelDispatcher.OnOpen(TimeSpan timeout)
    <ExceptionString>System.InvalidOperationException: The ChannelDispatcher at 'http://localhost:8000/MyService' with contract(s) '"IssueAndRenewSession"' is unable to open its IChannelListener. ---&amp;gt; System.InvalidOperationException: The service certificate is not provided. Specify a service certificate in ServiceCredentials.
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateServerX509TokenProvider()
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateLocalSecurityTokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenProvider(SecurityTokenRequirement requirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateTlsnegoServerX509TokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateTlsnegoSecurityTokenAuthenticator(RecipientServiceModelSecurityTokenRequirement recipientRequirement, Boolean requireClientCertificate, SecurityTokenResolver&amp;amp; sctResolver)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, SecurityTokenResolver&amp;amp; outOfBandTokenResolver)
    at System.ServiceModel.Security.SecuritySessionSecurityTokenAuthenticator.SessionRenewSecurityTokenManager.CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, SecurityTokenResolver&amp;amp; outOfBandTokenResolver)
    <InnerException><ExceptionType>System.InvalidOperationException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType><Message>The service certificate is not provided. Specify a service certificate in ServiceCredentials. </Message><StackTrace> at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateServerX509TokenProvider()
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateLocalSecurityTokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenProvider(SecurityTokenRequirement requirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateTlsnegoServerX509TokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateTlsnegoSecurityTokenAuthenticator(RecipientServiceModelSecurityTokenRequirement recipientRequirement, Boolean requireClientCertificate, SecurityTokenResolver&amp;amp; sctResolver)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, SecurityTokenResolver&amp;amp; outOfBandTokenResolver)
    at System.ServiceModel.Security.SecuritySessionSecurityTokenAuthenticator.SessionRenewSecurityTokenManager.CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, SecurityTokenResolver&amp;amp; outOfBandTokenResolver)
    at System.ServiceModel.Security.SymmetricSecurityProtocolFactory.OnOpen(TimeSpan timeout)
    at System.ServiceModel.Security.WrapperSecurityCommunicationObject.OnOpen(TimeSpan timeout)
    <ExceptionString>System.InvalidOperationException: The service certificate is not provided. Specify a service certificate in ServiceCredentials.
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateServerX509TokenProvider()
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateLocalSecurityTokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenProvider(SecurityTokenRequirement requirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateTlsnegoServerX509TokenProvider(RecipientServiceModelSecurityTokenRequirement recipientRequirement)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateTlsnegoSecurityTokenAuthenticator(RecipientServiceModelSecurityTokenRequirement recipientRequirement, Boolean requireClientCertificate, SecurityTokenResolver&amp;amp; sctResolver)
    at System.ServiceModel.Security.ServiceCredentialsSecurityTokenManager.CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, SecurityTokenResolver&amp;amp; outOfBandTokenResolver)
    at System.ServiceModel.Security.SecuritySessionSecurityTokenAuthenticator.SessionRenewSecurityTokenManager.CreateSecurityTokenAuthenticator(SecurityTokenRequirement tokenRequirement, SecurityTokenResolver&amp;amp; outOfBandTokenResolver)


    ----------------------------------------------------------------------------------------
    ----------------------------------------------------------------------------------------
    ----------------------------------------------------------------------------------------


    As far as i understand it complains that i don't have an x509 certificate installed in the system, but is there a way not to have it Otherwise making our customers install any kind of certs is gonna be a disaster. OR is there any other way of doing username authentication not involving certs

    Thank you,
    Andrey

  • Moridi

    Hi Govind,

    Thanks for your reply!

    If i use your approach to assign MessageCredentialType.Windows for ClientCredentialType then username validation doesn't happen which defeats the purpose.

    Sajay's code seems to work fine, i just need to test it more thorough and to try to understand how/why it works ;)

    Thank you,

    Andrey


  • stallion_alpa

    Hi Andrey,

    As the exception message states you need to configure a certificate for the service. You can do this in the service credentials as,

    service.Credentials.ServiceCertificate.SetCertificate(StoreLocation.CurrentUser, StoreName.My, X509FindType.FindBySubjectName, "Service.com");

    WCF will have to create a secure channel in order to send the username/password over to the service as we do not derive a key from a username token. Once a certificate is configured for the service WCF will do a SSL handshake with the service endpoint and retreive the service certificate and will create that secure channel for you.

    You need a certificate only for the service not for the clients. Is this still a problem for your customers Have you tried MessageCredentialType.Windows for ClientCredentialType This will not require a service certificate. Would that work for you

    Thanks,

    Govind



  • rskorski

    If you want to host this in a winform and use security you can use message security using windows. Take a look at this sample where you can actually use the username password as a supporting token.

    http://blogs.msdn.com/sajay/archive/2006/12/12/passing-a-username-as-a-supporting-token.aspx



  • philipsh

    Hi Sajay,

    In the code you kindly provided:

    http://blogs.msdn.com/sajay/archive/2006/12/12/passing-a-username-as-a-supporting-token.aspx

    you are creating a CustomBinding out of WSHttpBinding and are using that CustomBinding (instead of wshttpbinding) for the service. I tried to just use wshttpbinding and then username auth doesn't work - validatdor isn't called and connection is always allowed. Could you please explain me why does it work like that, and also, what do i do in case if i need to use callback which would require wshttpdualbinding

    Thank you in advance!

    Andrey


  • Junner2003

    Hi Sajay,

    Thanks for your reply!
    So is there anything i can do without using certs in my case (any other type of security i could involve to be able to use UserName authentication) Also, the service will be hosted in a WinForms app, not IIS, so I'm not sure if certificate can be used at all.
    Also, should certificate be installed just on the server or clients as well

    Sorry if my questions sound stupid or naive, but I'm pretty new to web services/wcf programming and still trying to get the concept.

    Any help/suggestions would be highly appreciated!

    Thank you,
    Andrey

  • AdrianGodong

    Hi Sajay,

    I just got a problem with using your approach (ClientCredentialType = Windows and CustomBinding) and i hope you could kindly help me: if i want to retrevive current username from within an contract method,

    i loop through claims and it only has my own windows username instead of the username i specified. I think that i'm doing it the wrong way but i don't see how to fix it:

    // ----------------- Contract Interface -------------

    [ServiceContract()]

    public interface IDocService

    {

    [OperationContract]

    void Login();

    [OperationContract]

    DataTable GetTreeNodeChildren(String sTreeNodeID);

    }

    // --------------------------------- Implementation ----------------------------------------------------

    public class DocServiceType: IDocService

    {

    public void Login() // If doesn't through an exception, it means user is validated

    {}

    public DataTable GetTreeNodeChildren(String sTreeNodeID)

    {

    CurrentUser();

    return null;//new DocumentTree().GetTreeNodeChildren(sTreeNodeID);

    }

    public void CurrentUser()

    {

    foreach (ClaimSet cs in OperationContext.Current.ServiceSecurityContext.AuthorizationContext.ClaimSets)

    {

    foreach (Claim claim in cs)

    {

    if (claim.ClaimType == ClaimTypes.Name && claim.Right == Rights.PossessProperty)

    {

    if (claim.ClaimType == ClaimTypes.Name && claim.Right == Rights.PossessProperty)

    {

    String sUsername = claim.Resource.ToString();} //////////////// HERE IS THE PROBLEM - IT HAS "MYDOMAIN\MYUSERNAME" instead of username specified in credentials

    }

    }

    }

    }

    }

    Do you know what is the right way to extract username for current connection from within an Operation Contract's method

    Thank you,

    Andrey


  • Kevin Dente

    Hi Andrey,

    Can you let us know what exception you are seeing You can enable tracing and that should tell you more on what might be wrong. You can look at http://blogs.msdn.com/govindr/archive/2006/11/01/debugging-wcf-traces-and-message-logs.aspx to see how to enable tracing.

    Thanks,

    - Govind



  • BobRS

    You need some kind of security on the transport or message.
    Your userName is a credential and not used for security. Basic statement is that you cant have a username token sent without any security. http://blogs.msdn.com/sajay/archive/2007/01/05/thoughts-on-basichttpbinding-security-and-ssl.aspx

    Let me know if this helps.



  • WCF UserName authentication