Nielsen's Weblog : .NET [use your Context dude]
Updated: 7/13/2007; 11:24:48 PM.

 

Subscribe to "Nielsen's Weblog" in Radio UserLand.

Click to see the XML version of this web page.

Click here to send an email to the editor of this weblog.

 
 

Friday, July 13, 2007

 

Say you have a service which requires a UsernameToken, the client would be responsible for passing in this token. Where do you get this token from, well usually the user is prompted for her credentials. There is the standard UI dialogbox for this kind of thing here.

               

So let's try and complicate things a bit by NOT just passing in the username and password on the proxy.ClientCredentials.UserName.UserName, oh no, not this time.

Now the WCF team have provided us with this little (rarely used imo) interface called IInteractiveChannelInitializer

 

This interface helps us implement the "lets bother the user with a tedious login box" but BEFORE a channel is opened, this is the key thing to remember here and the sole motivation for this interface.

 

The documentation on this interface is rather weak and I completely missed out on the fact that I had to implement a ClientCredentialsSecurityTokenManager also, so I got some hard to understand exceptions on my first implemention.

 

Okay the basic song goes like this, create a class that derives from ClientCredentials and override the ApplyClientBehavior by adding your own class which implements the IInteractiveChannelInitializer interface… and yes override the CreateSecurityTokenManager with your own SecurityTokenManager (I messed that part up initially).

 

So a sparse implementation of the ClientCredentials would turn out this way :

 

public class ClientCredentialsEx : ClientCredentials

{

    public ClientCredentialsEx(): base() {}

    public ClientCredentialsEx(ClientCredentialsEx other): base(other){}

    public override SecurityTokenManager CreateSecurityTokenManager()

    {

        return new MyClientCredentialsSecurityTokenManager(this);

    }

 

public override void ApplyClientBehavior(ServiceEndpoint serviceEndpoint, ClientRuntime behavior)

    {

            behavior.InteractiveChannelInitializers.Add(new ShowCredentialsUI());

            base.ApplyClientBehavior(serviceEndpoint, behavior);   

    }

    protected override ClientCredentials CloneCore()

    {

        return new ClientCredentialsEx(this);

    }

}

 

 

And then your implementation of the IInteractiveChannelInitializer

Would look something like this, note I didn't go all the way with the standard CreUIPromptForCredentials dialogbox, google it.

 

public class ShowCredentialsUI : IInteractiveChannelInitializer

{

    public IAsyncResult BeginDisplayInitializationUI(IClientChannel channel,

AsyncCallback callback, object state)

    {

        //call the CredUIPromptForCredentials and get back a user and password.

        ChannelParameterCollection col = channel.GetProperty<ChannelParameterCollection>();

col.Add(new System.Net.NetworkCredential(username, password));       

return new AsyncResult();

    }

 

    public void EndDisplayInitializationUI(IAsyncResult result)

    {

        AsyncResult ar = (AsyncResult)result;

        ar.isCompleted = true;

    }

}


 

 

Now a very sparse implemention of the IAsyncResult, I blogged about this long time ago.

 

public class AsyncResult : IAsyncResult

{

    public bool isCompleted = false;

 

    public object AsyncState

    {

        get { throw new Exception("The method or operation is not implemented."); }

    }

 

    public System.Threading.WaitHandle AsyncWaitHandle

    {

        get { throw new Exception("The method or operation is not implemented."); }

    }

 

    public bool CompletedSynchronously{get{return true;}}

 

    public bool IsCompleted { get { return isCompleted; }

    }

}

 

 

Now for the implementation of the custom ClientCredentialsSecurityTokenManager.

The crux of this is you would dig out the NetworkCredential from the ChannelParameterCollection which was set by the BeginDisplayInitializationUI.

 

class MyClientCredentialsSecurityTokenManager : ClientCredentialsSecurityTokenManager

{

    public MyClientCredentialsSecurityTokenManager(ClientCredentials parent): base(parent){}

   

    public override SecurityTokenProvider CreateSecurityTokenProvider(SecurityTokenRequirement tokenRequirement)

    {

        if (tokenRequirement.KeyUsage == SecurityKeyUsage.Signature)

        {

            NetworkCredential token = null;

            ChannelParameterCollection obj = (ChannelParameterCollection)tokenRequirement.Properties[ServiceModelSecurityTokenRequirement.ChannelParametersCollectionProperty];

            token = obj[0] as NetworkCredential;

            if (tokenRequirement.TokenType == SecurityTokenTypes.UserName)

            {

                return new MyUsernameSecurityTokenProvider(token);

            }

        }

        return base.CreateSecurityTokenProvider(tokenRequirement);

    }

 

My custom SecurityTokenProvider which just creates the UserNameSecurityToken for the channel to use.

 

    class MyUsernameSecurityTokenProvider : SecurityTokenProvider

    {

        NetworkCredential cre = null;

        public MyUsernameSecurityTokenProvider(NetworkCredential credentials)

        {

            this.cre = credentials;

        }

        protected override SecurityToken GetTokenCore(TimeSpan timeout)

        {

            UserNameSecurityToken token = new UserNameSecurityToken(cre.UserName,cre.Password);

            return token;

        }

    }

 

Now that was easy, all we need todo now is to hook up our new behavior on the proxy. This can be done in many ways, I choose to override the ClientBase which then would add our new "lets bother the user with a tedious login box". This is a matter of replacing the standard ClientCredentials class on the proxy behaviour, with our own IEndpointBehavior implemented by our custom ClientCredentialsEx class like this:

 

public partial class PartnerSubscriptionProxy : ClientBase<IPartnerSubscriptionService>, IPartnerSubscriptionService

{

        public PartnerSubscriptionProxy(string address)

        {

            Endpoint.Address = new EndpointAddress(address);

            Endpoint.Behaviors.Remove< ClientCredentials >();

            Endpoint.Behaviors.Add(new ClientCredentialsEx());

        }

......

 

Right so all you have to do now is just invoke the proxy and call out to a method, this will invoke our ui box before the channel is opened giving the user a chance to think real hard about her credentials (which btw. always can be found on that yellow slip on your desk).

"Some cause happiness wherever they go; others, whenever they go."
 --Oscar Wilde


8:45:57 PM    comment []

© Copyright 2007 Allan Nielsen.



Click here to visit the Radio UserLand website.
 


July 2007
Sun Mon Tue Wed Thu Fri Sat
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31        
Apr   Aug