Passing Extra Information in WS-Security UserNameToken

(This post is a continuation of the previous post Using WSE 3.0 for Web Service Authentication)

Sometimes, apart from the user credential information passed in the WSE UserNameToken we might need to pass additional informaion in the UserNameToken. For example, in a recent scenario I had to implement multiple levels of authentication. The consumer of the service was required to pass a service level username and password which it can pass in the UserNameToken and also the service required additional Business User Authentication, so the consumer is required to pass this additional Business User credentials.

The Microsoft Implementation of WSE allows you to insert extra xml in the UserNameToken using the UserNameToken.AnyElements property of the Microsoft.Web.Services3.Security.Tokens.UsernameToken class. See the following code which is adding the ExtraUser XML in the UserNameToken.

Dim U As New Microsoft.Web.Services3.Security.Tokens.UsernameToken(“<User_Name>”, “<Password>”, Security.Tokens.PasswordOption.SendHashed)
Dim xmldoc As New System.Xml.XmlDocument()
Dim xmlElement As System.Xml.XmlElement = xmldoc.CreateElement(“ExtraUser”)
Dim xmlElement1 As System.Xml.XmlElement = xmldoc.CreateElement(“UserId”)
xmlElement1.InnerText = “testuser”
Dim xmlElement2 As System.Xml.XmlElement = xmldoc.CreateElement(“UserPass”)
xmlElement2.InnerText = “123”
xmlElement.AppendChild(xmlElement1)
xmlElement.AppendChild(xmlElement2)
U.AnyElements.Add(xmlElement)

This username token can then be passed in the WS-Security. The Service Provider needs to modify its CustomTokenManager class to authenticate the ExtraUser information. The following UserNameToken passed in the SOAP envelop shows how the additional informaiton is passed with the UserNameToken.

<wsse:UsernameToken xmlns:wsu=”http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd” wsu:Id=”SecurityToken-2ee318dd-56ef-4f26-877d-2a199ff5b4e3″>
<wsse:Username>aleem</wsse:Username>
<wsse:Password Type=”
http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest”>CyI4oSXQgRMdYF416fHcD0IDpIE=</wsse:Password>
<wsse:Nonce>NVU8Adj9tEMZQpBPoPfJMw==</wsse:Nonce>
<wsu:Created>2007-09-18T13:10:30Z</wsu:Created>
<ExtraUser><UserId>testuser</UserId><UserPass>123</UserPass></ExtraUser>
</wsse:UsernameToken>

Using WSE 3.0 for Web Service Authentication

Web Services Enhancements (WSE) is a webservice standard which provides cross-platform security for webservices. WSE provides various security specifications like WS-Security, WS-Policy, WS-Trust, WS-Privacy, WS-SecureConversation, WS-Federation, WS-Authorization which can be used to provide authentication, authorization and message level security using digital signing and encryption based on particular requirements on hand. More information on WSE and the areas it addresses can be seen here (This document is a old but provides a an overview of WSE security-model). Another good article on MSDN Building Secure ASP.NET Applications: Authentication, Authorization, and Secure Communication explain different security scenarios and how WSE can be used in each one of them.

In the following I will provide a step-by-step detail on how to use WSE 3.0 for authenticating webservices with your custom user database. The custom database can be an ASP.NET Membership database or other custom user database. (You must have Visual Studio 2005 installed to proceed with the following. Please click on the images to enlarge)

1. Download WSE 3.0 from http://www.microsoft.com/downloads/details.aspx?FamilyID=018A09FD-3A74-43C5-8EC1-8D789091255D&displaylang=en and install it on your machine.

2. Create a new ASP.NET Webservice website project. Right Click the project select the “WSE Settings 3.0” option as shown below.

3. This will open the following WSE settings dialog. Select the “Enable this project for WSE” and “Enable Microsoft WSE Soap Protocol Factory” from the settings dialog.

4. Go to the Policy Tab and select “Enable Policy”. Add a new Policy with the name “CustomServicePolicy” (or any name).

5. After you add a new Policy, the following Policy settings wizard will open and you will be required to define your policy.

6. Select the Authentication method. The Authentication method can be Anonymous, Username, Certificate and Windows. In this sample I am going to select the username as we care going to pass username and password for the authentication. You can use other methods like windows if required. Also select that you are seting this policy for a service. (In case you are creating a policy for a client to access WSE webservice you can select the “Secure Client Application” option). Click next to proceed.

7. In the following screen you can select “Enable WS-Security 1.1 Extensions” and select Protection Order as “None”. You can select other protection methods if you want to sign and encrypt your messages using a X.25 certificate.

8. Click next to proceed, you will be shown the following screen with the informaiton about the Policy that you have defined.

9. After defining the Policy, go to you service class and add this Policy to the class using the Policy Attribute as shown in the following screenshot. You will be required to add Microsoft.Web.Services3.dll into your project to reference Policy attribute and other WSE classes. The WSE documentation says that this reference will be automatically added to your project if you enable WSE for your project as we did but I have observed that you have to manually add this reference and it is not added automcatically. You can get this dll from the following path C:\Program Files\Microsoft WSE\v3.0\ (The path may be different based on your installation location).

Note that after configuring WSE settings, there is an extra configuration file “wse3policyCache.config” which has been added to the project.

10. Now we need to write a Custom Token Manager which will authenticate the username and password sent by the user. Add a new Class “CustomTokenManager” in the project and inherit it from Microsoft.Web.Services3.Security.Tokens.UsernameTokenManager. Override the AuthenticateToken method, WSE soap filter will call this method to obtain the clear text password for the user. You can write your custom code to access your ASP.NET membership database or other user database to obtain the password. You can get the username from the UsernameToken passed as a parameter to the AuthenticateToken method.

11. After you have you CustomTokenManager ready, you need to set this in web.config in the WSE security settings so WSE can use this class when user authentication is required.

 

Now your service is ready to use and it will authenticate the incoming user with your custom database usign the CustomTokenManager class that you have created.

To write a client for a WSE service, you need to have WSE 3.0 installed on the client machine. Moreover, you will have to enable WSE for your project (step 3) before adding the webreference to the server. If WSE is installed on the client machine and is enabled for your project, the WSDL.exe toll will generate an additional proxy class ending with Wse if the service supports WSE. In our case there will be two proxy classes that will be generated on the client. Service and ServiceWse. Use ServiceWse for accessing the service.

The following code shows how to pass username and password as UserNameToken in WS-Security header.

Dim oService As New WSETestService.ServiceWse

Dim U As New Microsoft.Web.Services3.Security.Tokens.UsernameToken(“<User_Name>”, “<Password>”, Security.Tokens.PasswordOption.SendHashed)
oService.SetClientCredential(U)

Dim oPolicy As New Policy()
oPolicy.Assertions.Add(New UsernameOverTransportAssertion())
oService.SetPolicy(oPolicy)

oService.HelloWorld()

Following is the SOAP request with the UserNameToken in the WSE header with username and password of the user. The password is passed as SHA-1 hash of the actual password. (Note that you selected Security.Tokens.PasswordOption.SendHashed inUserNameToken constructor from the client when creating the token, you can choose to send clear text password as well)

<inputMessage utc=”9/18/2007 1:10:30 PM” messageId=”urn:uuid:8c890d37-28a0-423f-8aaa-1b35e7f6a021″>
<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://tempuri.org/HelloWorld</wsa:Action>
<wsa:MessageID>urn:uuid:8c890d37-28a0-423f-8aaa-1b35e7f6a021</wsa:MessageID>
<wsa:ReplyTo>
<wsa:Address>
http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:Address>
</wsa:ReplyTo>
<wsa:To>
http://localhost:1841/WebServiceAuthentication/Service.asmx</wsa:To>
<wsse:Security soap:mustUnderstand=”1″>
<wsu:Timestamp wsu:Id=”Timestamp-dd031f35-cc14-4426-b34f-bfff66d03991″>
<wsu:Created>2007-09-18T13:10:30Z</wsu:Created>
<wsu:Expires>2007-09-18T13:15:30Z</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-2ee318dd-56ef-4f26-877d-2a199ff5b4e3″>
<wsse:Username>aleem</wsse:Username>
<wsse:Password Type=”
http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordDigest”>CyI4oSXQgRMdYF416fHcD0IDpIE=</wsse:Password>
<wsse:Nonce>NVU8Adj9tEMZQpBPoPfJMw==</wsse:Nonce>
<wsu:Created>2007-09-18T13:10:30Z</wsu:Created>
</wsse:UsernameToken>
</wsse:Security>

</soap:Header>
<soap:Body>
<HelloWorld xmlns=”
http://tempuri.org/” />
</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=”Exited SOAP filter Microsoft.Web.Services3.Design.UsernameOverTransportAssertion+ServiceInputFilter” />
<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 />
<soap:Body>
<HelloWorld xmlns=”
http://tempuri.org/” />
</soap:Body>
</soap:Envelope>
</processingStep>
</inputMessage>

Deleting Locked Workspace from Team Foundation Server

One thing I have noticed over the last year since I have started working with TFS is that it is highly sensitive to any changes/issues in the enviornment and easily gets currupted. However, TFS works wonderfully over the VPN connections far better than VSS. We initially struggled a lot on VSS while working remotely and it used to take a long time to check-in and would actually die if you were to take the latest of the whole project.

Anyway I got my solution corrupted due to some changes in the VPN settings in India Office, and was really stuck what to do about my already checked-out and locked files. I could restore the files one by one (they were only a few) but TFS got my old workspace locked and would’nt allow me to modify the files which are locked in the other workspace.

The solution is to unlock all files under a specific workspace and delete the old workspace. (This is only in case you are sure you can restore your changes manually). The Following TFS commands can be used

tf lock /lock:none /server:<server_name> /workspace:<my current workspace>;<domain>\<user> $/<project_name>

tf workspace /server:<server_name> /delete <my current workspace>;<domain>\<user>

If you do not know your workspace name or the previous user id you can list all tfs workspaces using the following.

tf workspaces /server:<server_name> /owner:*

Unfortunately, there are no options available in Visual Studio 2005 to do this, the workspace management option available in Visual Studio is extremely limited.

Czech Republic Flag !

All Things Pakistan always has good posts about the facets of pakistani society, politics and culture, often serious but sometimes the mockery of the issues which makes it even more ineteresing to read.

I just saw this post Welcoming the Czech Prime Minister and I could not stop laughing at the negligence  and incompetency of our government infrastructure.