Groups | Blog | Home
all groups > dotnet web services enhancements > june 2006 >

dotnet web services enhancements : WSE 3.0, usernameOverTransportSecurity, custom Token Manager w/ securityTokenManager,


Frank Villasenor
6/26/2006 10:07:21 AM
Hello all,
I'm attempting to learn how to use WSE 3.0 with Visual Studio 2005.
I've read a lot of material about WSE 3.0 and I've started to grasp how
this libarary works.

Although, much closer than I was a few days ago I'm not in at a point
where I believe everything is configured properly, but since my
solution isn't working it must not be. I really could use some help....

My using:
Visual Studio 2005,
WSE 3.0
usernameOverTransportSecurity
A custom UsernameTokenManager. (I want to authenticate against an
existing database)

In the UsernameTokenManager i've derived a class and have overridden
the AuthenticateToken method. I'm still developing the method and have
not "finished it." On my validCreditials variable, I've simply set it
to true. Which should allow me to authenticate any user that I throw at
it. (Please note, I've put this code in a separate project with it's
own namespace.)

Below is my code:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
protected override string AuthenticateToken( UsernameToken token )
{
System.Diagnostics.Debug.WriteLine( "Starting:
RmsTokenManager.AuthenticateToken(...)" );

string userName = token.Username;
string sPassword = token.Password;

bool validCreditials = true; // Validate creditionals with some
method. (SQL Wrapper and stored proc.)

if (!validCreditials)
{
System.Diagnostics.Debug.WriteLine( "Auth Failed.
RmsTokenManager.AuthenticateToken(...)" );
OnLogonUserFailed( token );
}
else
{
System.Diagnostics.Debug.WriteLine( "Auth succeeded.
RmsTokenManager.AuthenticateToken(...)" );
GenericIdentity oIdentity = new GenericIdentity( token.Username
);
GenericPrincipal oPrincipal = new GenericPrincipal( oIdentity,
new string[] { "User" } );

token.Principal = oPrincipal;
}

//
// Return token.Password like the base (overriden function)
return token.Password;
}
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


For my Web.Config file, for my web serivce I have:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
<!--
START: Configuration for the WSE.
-->
<microsoft.web.services3>
<policy fileName="wse3policyCache.config" />
<security>
<securityTokenManager>
<!-- <clear /> -->
<add localName="UsernameToken"
type="MicroTek.ImageVerification.Security.RmsTokenManager,
MicroTek.ImageVerification.Security"
namespace="http://docs.oasisopen.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
/>
</securityTokenManager>
</security>
<diagnostics>
<trace enabled="true" input="InputTrace.webinfo"
output="OutputTrace.webinfo" />
<detailedErrors enabled="true" />
</diagnostics>
</microsoft.web.services3>
<!--
END: Configruation for the WSE.
-->
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-


For my Policy file, for the web service I have:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">
<extensions>
<extension name="usernameOverTransportSecurity"
type="Microsoft.Web.Services3.Design.UsernameOverTransportAssertion,
Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" />
<extension name="requireActionHeader"
type="Microsoft.Web.Services3.Design.RequireActionHeaderAssertion,
Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" />
</extensions>
<policy name="Policy1">
<authorization>
<allow role="User" />
<deny role="*" />
</authorization>
<usernameOverTransportSecurity />
<!--
<requireActionHeader />
-->
</policy>
</policies>
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-




Now for the client. The client is to be a smart client application.
(Windows Forms). I had the client application working against the web
service without any WSE. It was working great. But, I needed the
security so I added WSE in to the mix.

The configuration for the client application:
I use a proxy to wrap the generated proxy. The "proxy" class simply
performs authentication tasks and allows me to pass the web service
variable around to different parts of my program. I can't post the
entire part of the program because there would be too much code. But
the initialization of the web service is:


=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
//
// Class fields.
private string sUrl;
private localhost.ServiceWse _WebServiceWse;
private string sUserName;
private string sPassword;

public WebServiceProxy( string psWebServiceURL, string psUserName,
string psPassword )
: this()
{
if (psWebServiceURL.Length == 0 || psUserName.Length == 0 ||
psPassword.Length == 0 )
{
throw new ApplicationException( "psWebServiceURL can not be a
blank string!" );
}

sUrl = psWebServiceURL;
sUserName = psUserName;
sPassword = psPassword;

_WebServiceWse = new
MicroTek.ImageVerification.localhost.ServiceWse();
_WebServiceWse.Url = sUrl;
_WebServiceWse.UseDefaultCredentials = false;

_WebServiceWse.PreAuthenticate = true;
SetWebServiceUserToken();
_WebServiceWse.SetPolicy( "Policy1" );
}

private void SetWebServiceUserToken()
{
if (sUserName == null)
{
throw new ArgumentNullException( "UserName" );
}
if (sPassword == null)
{
throw new ArgumentNullException( "Password" );
}

UsernameToken _unt = new UsernameToken( sUserName, sPassword,
PasswordOption.SendPlainText );
_WebServiceWse.SetClientCredential( _unt );
}

public bool ValidateImage( int piReservationID, string psImageGUID,
string psClientSpecs )
{
psClientSpecs = HttpUtility.HtmlEncode( psClientSpecs );
return _WebServiceWse.ValidateImage( piReservationID,
psImageGUID, psClientSpecs );
}
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

Once initialized, I can call any web service method from the
WebServiceProxy instance. My client configuration is as follows:

app.config:
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-
<microsoft.web.services3>
<policy fileName="wse3policyCache.config" />
<diagnostics>
<trace enabled="true" input="InputTrace.webinfo"
output="OutputTrace.webinfo" />
Frank Villasenor
6/26/2006 1:14:28 PM
Thank you for your post. I have stepped through the code and they are
executing. Also, I've downloaded and looked at the example you mention.
I tried to sync my code up to the example code however there is
difference in the sample, as it uses an x.509 cert.

I will take another look at the example to see if I've overlooked
anything.

Also, as a side note; I've tried to recall the
SetWebServiceUserToken(); method. I was try to make logic adjustments.
It didn't work, but would resetting the credentials make any difference
at all?

Thank you for going through all of that. I know there was a lot in
there.

Frank
http://www.TheOpenSourceU.com

[quoted text, click to view]
Frank V
6/26/2006 1:47:27 PM
Hey Pablo,
I've added the web service call directly to my Data binding method
(see code snippt) and I am now getting the user name and password
(among other things) in side the message. But I'm an still getting the
same error that I was previously.

In case it will help, I will add the Client and Server Output Trace and
INput trace, respetively.

----------------------------------------------------------------
Client, Output Trace:
----------------------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<log>
<outputMessage utc="6/26/2006 8:25:26 PM"
messageId="urn:uuid:afb53a33-fd73-449e-989a-6d8dbcd38b16">
<processingStep description="Unprocessed message">
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema">
<soap:Body>
<ZipCodeSearch xmlns="http://www.MyCompany.com/WSImgVer/">
<psZipCode>60606</psZipCode>
</ZipCodeSearch>
</soap:Body>
</soap:Envelope>
</processingStep>
<processingStep description="Entering SOAP filter
Microsoft.Web.Services3.Design.UsernameOverTransportAssertion+ClientOutputFilter"
/>
<processingStep description="Exited SOAP filter
Microsoft.Web.Services3.Design.UsernameOverTransportAssertion+ClientOutputFilter"
/>
<processingStep description="Processed message">
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<soap:Header>

<wsa:Action>http://www.MyCompany.com/WSImgVer/ZipCodeSearch</wsa:Action>

<wsa:MessageID>urn:uuid:afb53a33-fd73-449e-989a-6d8dbcd38b16</wsa:MessageID>
<wsa:ReplyTo>

<wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>http://localhost:1346/WSImgVer/Service.asmx</wsa:To>
<wsse:Security soap:mustUnderstand="1">
<wsu:Timestamp
wsu:Id="Timestamp-a4b53d14-1c79-4fc4-99e5-363f4fb1fca9">
<wsu:Created>2006-06-26T20:25:26Z</wsu:Created>
<wsu:Expires>2006-06-26T20:30:26Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
wsu:Id="SecurityToken-ec8ec540-596d-4b2a-9b60-e7cbc638ec59">
<wsse:Username>MyUserName</wsse:Username>
<wsse:Password
Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">ThePassword</wsse:Password>
<wsse:Nonce>imruhM3LhcjDJxU9mvZTmg==</wsse:Nonce>
<wsu:Created>2006-06-26T20:25:26Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<ZipCodeSearch xmlns="http://www.MyCompany.com/WSImgVer/">
<psZipCode>60606</psZipCode>
</ZipCodeSearch>
</soap:Body>
</soap:Envelope>
</processingStep>
</outputMessage>
</log>


----------------------------------------------------------------
Web Service - Server Input:
----------------------------------------------------------------
<?xml version="1.0" encoding="utf-8"?>
<log>
<inputMessage utc="6/26/2006 8:25:26 PM"
messageId="urn:uuid:afb53a33-fd73-449e-989a-6d8dbcd38b16">
<processingStep description="Unprocessed message">
<soap:Envelope
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:wsa="http://schemas.xmlsoap.org/ws/2004/08/addressing"
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
<soap:Header>

<wsa:Action>http://www.MyCompany.com/WSImgVer/ZipCodeSearch</wsa:Action>

<wsa:MessageID>urn:uuid:afb53a33-fd73-449e-989a-6d8dbcd38b16</wsa:MessageID>
<wsa:ReplyTo>

<wsa:Address>http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>http://localhost:1346/WSImgVer/Service.asmx</wsa:To>
<wsse:Security soap:mustUnderstand="1">
<wsu:Timestamp
wsu:Id="Timestamp-a4b53d14-1c79-4fc4-99e5-363f4fb1fca9">
<wsu:Created>2006-06-26T20:25:26Z</wsu:Created>
<wsu:Expires>2006-06-26T20:30:26Z</wsu:Expires>
</wsu:Timestamp>
<wsse:UsernameToken
xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
wsu:Id="SecurityToken-ec8ec540-596d-4b2a-9b60-e7cbc638ec59">
<wsse:Username>MyUserName</wsse:Username>
<wsse:Password
Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">ThePassword</wsse:Password>
<wsse:Nonce>imruhM3LhcjDJxU9mvZTmg==</wsse:Nonce>
<wsu:Created>2006-06-26T20:25:26Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>
</soap:Header>
<soap:Body>
<ZipCodeSearch xmlns="http://www.MyCompany.com/WSImgVer/">
<psZipCode>60606</psZipCode>
</ZipCodeSearch>
</soap:Body>
</soap:Envelope>
</processingStep>
<processingStep description="Entering SOAP filter
Microsoft.Web.Services3.Design.RequireSoapHeaderAssertion+RequireSoapHeaderFilter"
/>
<processingStep description="Exited SOAP filter
Microsoft.Web.Services3.Design.RequireSoapHeaderAssertion+RequireSoapHeaderFilter"
/>
<processingStep description="Entering SOAP filter
Microsoft.Web.Services3.Design.UsernameOverTransportAssertion+ServiceInputFilter"
/>
<processingStep description="Exception thrown: UsernameToken is
expected but not present in the security header of the incoming
message."> at
Microsoft.Web.Services3.Design.UsernameOverTransportAssertion.ServiceInputFilter.ValidateMessageSecurity(SoapEnvelope
envelope, Security security)
at
Microsoft.Web.Services3.Security.ReceiveSecurityFilter.ProcessMessage(SoapEnvelope
envelope)
at Microsoft.Web.Services3.Pipeline.ProcessInputMessage(SoapEnvelope
envelope)</processingStep>
</inputMessage>
</log>

----------------------------------------------------------------
Finally, the Output Trace from the Server (in case it will help)
----------------------------------------------------------------
Pablo Cibraro
6/26/2006 3:55:27 PM
Hi Frank,

Are the lines below being executed ?.

SetWebServiceUserToken();
_WebServiceWse.SetPolicy( "Policy1" );

The code looks fine at first glance and these lines are responsible to add
the username token to the message.

My advice is to use the sample provided by the Pattern & Practices team in
this gotdotnet workspace, (They already provide a working sample of username
authentication using a dabatabase).
http://www.gotdotnet.com/codegallery/codegallery.aspx?id=67f659f6-9457-4860-80ff-0535dffed5e6

The name of the sample is DirectAuthenticationWithDatabase and you can find
it in the zip file WSSP.zip

Let me know if I can help you with something else regarding this problem.

Regards,
Pablo Cibraro
http://weblogs.asp.net/cibrax


[quoted text, click to view]
Frank V
6/27/2006 7:38:42 AM
Hey Pablo,

Thank you for the info and the code. I will give this a try and see
what I come up with.

Regards,
Frank V.


[quoted text, click to view]
Pablo Cibraro
6/27/2006 10:14:22 AM
Hmm, that is really strange. If the username token is already added in the
message, the assertion should work without problems.

Try the following code, it is a custom UsernameOverTransport assertion (You
have to use this assertion instead of the WSE UsernameOverTransport
assertion)

namespace WSEUsername

{

public class MyCustomAssertion : UsernameOverTransportAssertion

{

public override Microsoft.Web.Services3.SoapFilter
CreateServiceInputFilter(FilterCreationContext context)

{

return new MyServiceInputFilter(this);

}

protected class MyServiceInputFilter : ServiceInputFilter

{

public MyServiceInputFilter(MyCustomAssertion assertion)

: base(assertion)

{

}

public override void
ValidateMessageSecurity(Microsoft.Web.Services3.SoapEnvelope envelope,
Microsoft.Web.Services3.Security.Security security)

{

UsernameToken utoken = null;

foreach (SecurityToken token in security.Tokens)

{

if (token is UsernameToken)

{

utoken = token;

break;

}

}

if (utoken == null)

{

throw new Exception("The username token is not present in the message");

}

base.ValidateMessageSecurity(envelope, security);

}

}

}

}

This assertion is only valid to see whether you are receiving the username
token in the service or not. (Test purposes)

Regards,
Pablo.


[quoted text, click to view]
Frank V
6/27/2006 3:09:36 PM
Hey Pablo,
I've been playing around with this almost all day to no avail.(The
test class that you put out there) I keep getting "Server unavailable,
please try later" even though it is actually available. I'm pretty
sure that it has something to do with my attempt to add the new
assertion in to the pipeline. Either the class itself or my
configuration. But I'm leaning towards my configuration to be the
problem.

Any insight would be appreciated.

Here is the XML from the Policy config file. You'll see that I've
commented out the my original. Here is the XML:

<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">
<extensions>
<!--
<extension name="usernameOverTransportSecurity"
type="Microsoft.Web.Services3.Design.UsernameOverTransportAssertion,
Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" />
-->

<extension name="MyCustomAssertion"
type="WSEUsername.MyCustomAssertion,
MyCompany.MyProgram.UsernameTokenManager" />
<extension name="requireActionHeader"
type="Microsoft.Web.Services3.Design.RequireActionHeaderAssertion,
Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" />
</extensions>
<policy name="Policy1">
<usernameOverTransportSecurity />
<requireActionHeader />
</policy>
</policies>

Thanks again,
Frank V.
www.TheOpenSourceU.com
JawzX01[.@.]gmail.com

[quoted text, click to view]
Frank V
6/28/2006 7:14:08 AM
I've tried that also, which I meant to include in my post, but either
way I receive the same error.

However, now that I know that usernameOverTransportSecurity is the
correct extention name, I will give this a try again and see if I can
find the problem.

Thanks for your help, again!
-Frank

[quoted text, click to view]
Frank V
6/28/2006 8:36:00 AM
Pablo,

A little update, I now have the web service configured properly and the
web service will run with the custom assertion.

Using the custom Assertion "MyCustomAssertion" (that you gave me) I
receive the message: "The username token is not present in the message"
(the exception).

I'm not sure how to proceed...

Regards,
Frank

[quoted text, click to view]
Frank V
6/28/2006 9:22:30 AM
Yeah, I'm not sure how to proceed either. I've been at this for days
now without getting anywhere....

Thank you for your help, I will keep working on this.

Regards,
Frank

[quoted text, click to view]
Pablo Cibraro
6/28/2006 10:01:09 AM
I think the problem is in the extension name, it should be
"usernameOverTransportSecurity" instead of "MyCustomAssertion".

<policies xmlns="http://schemas.microsoft.com/wse/2005/06/policy">
<extensions>
<extension name="usernameOverTransportSecurity"
type="WSEUsername.MyCustomAssertion,
MyCompany.MyProgram.UsernameTokenManager" />
<extension name="requireActionHeader"
type="Microsoft.Web.Services3.Design.RequireActionHeaderAssertion,
Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral,
PublicKeyToken=31bf3856ad364e35" />
</extensions>

<policy name="Policy1">
<usernameOverTransportSecurity />
<requireActionHeader />
</policy>
</policies>

If the problem still continue after making that change, you should check the
event viewer to see the real problem. (WSE usually logs the real error
there).

Regards,
Pablo.

[quoted text, click to view]
Pablo Cibraro
6/28/2006 12:00:16 PM
Hmm, I see. In that case, the client is not adding the username token at
all. That may happen for three reasons:

1. The usernameOverTransport assertion is not being executed on the client
2. A wrong assertion is being executed on the client
3. An username token was not being added in the proxy.

I have already taken a look to your code but I seems to be ok, I am not sure
how to proceed to find out the problem.

Regards,
Pablo.

[quoted text, click to view]