Click here to Skip to main content
15,860,844 members
Articles / Programming Languages / C#
Article

WebService Authentication with UsernameToken in WSE 3.0

Rate me:
Please Sign up or sign in to vote.
4.75/5 (20 votes)
12 Jul 20075 min read 276K   3.8K   78   48
Passing a username and password as plain text

Introduction

When I started working with WSE3, I just wanted to test how to authenticate a web service call with a username and password. I read the Implementing Direct Authentication with UsernameToken in WSE 3.0 article and I found it was very useful will all of the issues regarding to security. Wait a minute. It was so complicated for a newbie like me. I didn't care how to secure the message between client and web services at this time yet. I just wanted to pass a username and password as plain text and the web service to do authentication for each method call. I will leave the message encrypting tasks between client and web service for another article.

Background

Here are some good articles:

I assume you've already installed Web Services Enhancements (WSE) 3.0.

Using the Code

UsernameToken with username and password will be attached to the SOAP header when the client makes a call to the web service. To do custom authentication at server side, you need to override the AuthenticateToken method of the UsernameTokenManager class. We will create a class library project called UsernameAssertionLibrary and add a class called ServiceUsernameTokenManager that inherits from UsernameTokenManager.

C#
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;
using System.Xml;

using Microsoft.Web.Services3;
using Microsoft.Web.Services3.Design;
using Microsoft.Web.Services3.Security;
using Microsoft.Web.Services3.Security.Tokens;

namespace UsernameAssertionLibrary
{
    public class ServiceUsernameTokenManager : UsernameTokenManager
    {
        /// <summary />
        /// Constructs an instance of this security token manager.
        /// </summary />
        public ServiceUsernameTokenManager()
        {
        }

        /// <summary />
        /// Constructs an instance of this security token manager.
        /// </summary />
        /// <param name="nodes" />An XmlNodeList containing XML elements from a configuration file.</param />
        public ServiceUsernameTokenManager(XmlNodeList nodes)
            : base(nodes)
        {
        }

        /// <summary />
        /// Returns the password or password equivalent for 
        /// the username provided.
        /// </summary />
        /// <param name="token" />The username token</param />
        /// <returns />The password (or password equivalent) for the 
        /// username</returns />
        protected override string AuthenticateToken(UsernameToken token)
        {
            string username = token.Username;

            // it's up to you where you will get a password for some user
            // you may:
            // 1) get the password hash from web.config or system registry
            //    if you are implementing per-server security
            // 2) get the password from the database or 
            // XML file for the given user name

            // for example purposes we just return a reversed value of 
            // username
            char[] ch = username.ToCharArray();
            Array.Reverse(ch);
            return new String(ch);
        }

    }
}

The AuthenticateToken method will be called by WSE3 to retrieve the password of token.Username. You can implement this method to get a password from database, XML file, etc. for the given username. WSE3 will check if the password returned by AuthenticationToken matches with the password in the SOAP header. If they don't match, an exception will be sent to the client.

Now that we have the UsernameAssertionLibrary DLL, we need to register it to our web service. Add an ASP.NET web service project to the solution. We need to add reference to the UsernameAssertionLibrary class library to the web service project or we can copy UsernameAssertionLibrary.dll to the bin directory of the web service folder.

To enable WSE 3.0, right click on the web service project and click WSE Settings 3.0.

  1. Under the General tab, check both Enable this project for Web Services Enhancements and Enable Microsoft Web Services Enhancement Soap Protocol Factory.
  2. Under the Security tab, in the Security Tokens Managers area, click Add.
    Fill in the fields as follows:
    1. Type: "UsernameAssertionLibrary.ServiceUsernameTokenManager, UsernameAssertionLibrary"
    2. Namespace: "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
    3. LocalNode: "UsernameToken"
    4. Click OK. By doing this, you register our custom username token manager with WSE3.0.
  3. Under the Policy tab, check Enable Policy and click Add. Name your new application policy "ServerPolicy" and then click OK. In the WSE Security Settings Wizard window:
    1. Select Secure a service application and Username; then click Next
    2. Check Specify Username Token in code and click Next
    3. Select None (reply on transport protection), click Next and Finish
    Don't worry about the setting for Protection Order. We just make it work first before we can think how we should protect information sent via the network. There are several way you can secure your message -- for example, using SSL or WSE3.0 to encrypt the SOAP message -- but it is not in this very first look at WSE3.0.
  4. Under the Diagnostics tab, select Enable Message Trace. Enter InputTrace.webinfo into the Input text box and OutputTrace.webinfo into the Output text box. These are XML files you can use to trace SOAP messages sent from the client and service.

Now that we have wse3policyCache.config, take a look at this file:

C#
<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="ServerPolicy">
        <usernameOverTransportSecurity />
       <requireActionHeader />
    </policy>
</policies>

Open the web.config file; it looks like this:

C#
<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <configSections>
        <!-- Register WSE config section -->
        <section name="microsoft.web.services3" type=
            "Microsoft.Web.Services3.Configuration.WebServicesConfiguration,
            Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, 
            PublicKeyToken=31bf3856ad364e35" />
    </configSections>
    <appSettings />
    <connectionStrings />
    <system.web>
        <!-- Compilation settings -->
        <compilation debug="true">
            <assemblies>
                <add assembly="Microsoft.Web.Services3, Version=3.0.0.0, 
                     Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
                <add assembly="System.Security, Version=2.0.0.0, 
                     Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
                <add assembly="System.Configuration.Install, 
                     Version=2.0.0.0, Culture=neutral, 
                     PublicKeyToken=B03F5F7F11D50A3A" />
                <add assembly="System.Design, Version=2.0.0.0, 
                     Culture=neutral, PublicKeyToken=B03F5F7F11D50A3A" />
            </assemblies>
        </compilation>
        <!-- We don't require any authentication, because it will be 
            implemented on application layer -->
        <authentication mode="None" />
        <!-- Web Services settings -->
        <webServices>
            <!-- Enable WSE 3.0 -->
            <soapExtensionImporterTypes>
            <add type=
                "Microsoft.Web.Services3.Description.WseExtensionImporter, 
                Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, 
                PublicKeyToken=31bf3856ad364e35" />
            </soapExtensionImporterTypes>
            <soapServerProtocolFactory 
                type="Microsoft.Web.Services3.WseProtocolFactory, 
                Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, 
                PublicKeyToken=31bf3856ad364e35" />
        </webServices>
    </system.web>
    <!-- WSE 3.0 config section -->
    <microsoft.web.services3>
        <diagnostics>
            <trace enabled="true" input="InputTrace.webinfo" 
                output="OutputTrace.webinfo" />
        </diagnostics>
        <security>
            <securityTokenManager>
                <add type=
                    "UsernameAssertionLibrary.ServiceUsernameTokenManager, 
                    UsernameAssertionLibrary" namespace=
                    "http://docs.oasis-open.org/wss/2004/01/
                    oasis-200401-wss-wssecurity-secext-1.0.xsd" 
                    localName="UsernameToken" />
            </securityTokenManager>
        </security>
        <policy fileName="wse3policyCache.config" />
    </microsoft.web.services3>
</configuration>

Now we need to code for our web service. Add reference to Microsoft.Web.Services3 to the project. Open Service.cs and copy the code:

C#
using System;
using System.Web;
using System.Web.Services;
using System.Web.Services.Protocols;
using Microsoft.Web.Services3;

[WebService(Namespace = "http://tempuri.org/")]
[WebServiceBinding(ConformsTo = WsiProfiles.BasicProfile1_1)]
[Microsoft.Web.Services3.Policy("ServerPolicy")]
public class Service : System.Web.Services.WebService
{
    public Service () 
   {

        //Uncomment the following line if using designed components 
        //InitializeComponent(); 
    }

    [WebMethod]
    public string HelloMyFriend() 
    {
        //Say hello to the guy that you know
        return "Hello " + 
            RequestSoapContext.Current.IdentityToken.Identity.Name;
    }   
}

The attribute [Microsoft.Web.Services3.Policy("ServerPolicy")] will register our ServerPolicy, which is configured in wse3policyCache.config to the service. We have a web method called HelloMyFriend() that returns a string Hello + the username in the token, which is passed in the SOAP header when the client makes a HelloMyFriend call. There is no code in the HelloMyFiend method to authenticate the username, but WSE will call AuthenticateToken() from our custom Username Token Manager to retrieve the password. It will then compare the password with the one in SOAP header sent from the client.

Now we will see how we can have username and password in the SOAP header when we make a service call. Add a Console Application project to our solution called TestWebService and add reference to Microsoft.Web.Services3. Before you can make a call to the web service, you need to have a proxy. You can either use Add Web Preference in Visual Studio or use WseWsdl3.exe to generate a proxy class. I used WseWsdl3.exe; the default is in Program Files\Microsoft WSE\v3.0\Tools. Here is the command to generate the ServiceProxy.cs class.

wsewsdl3 <a href="http://localhost:1515/WebService/Service.asmx?wsdl">http://localhost:1515/WebService/Service.asmx?wsdl</a> 
    /o:c:\ServiceProxy.cs /l:cs /type:webClient 

Add ServiceProxy.cs to our TestWebService project. Like the server side, the client also needs to have a policy. You need to configure the client to enable WSE3.0. To enable WSE 3.0, right click on the TestWebService project and click WSE Settings 3.0.

  1. Under the General tab, check Enable this project for Web Services Enhancements.
  2. Under the Policy tab, check Enable Policy and click Add. Name your new application policy "ClientPolicy" and click OK. In the WSE Security Settings Wizard window:
    1. Select Secure a client application and Username and click Next
    2. Check Specify Username Token in code and click Next
    3. Select None (reply on transport protection), click Next and Finish
  3. Under the Diagnostics tab, select Enable Message Trace. Enter InputTrace.webinfo into the Input text box and OutputTrace.webinfo into the Output text box.

We have wse3policyCache.config in the project look like this:

C#
<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="ClientPolicy">
        <usernameOverTransportSecurity />
        <requireActionHeader />
    </policy>
</policies>

The app.config file looks like this:

C#
<configuration>
    <configsections>
        <section name="microsoft.web.services3" type=
            "Microsoft.Web.Services3.Configuration.WebServicesConfiguration,
            Microsoft.Web.Services3, Version=3.0.0.0, Culture=neutral, 
            PublicKeyToken=31bf3856ad364e35" />
    </configsections>
    <microsoft.web.services3>
        <diagnostics>
            <trace output="OutputTrace.webinfo" input="InputTrace.webinfo" 
                enabled="true" />
        </diagnostics>
        <policy filename="wse3policyCache.config" />
    </microsoft.web.services3>
</configuration>

Here is the client code:

C#
using System;
using System.Collections.Generic;
using System.Text;

using Microsoft.Web.Services3;
using Microsoft.Web.Services3.Design;
using Microsoft.Web.Services3.Security.Tokens;


namespace TestWebService
{
    class Program
    {
        static void Main(string[] args)
        {
            //init web service proxy 
            Service serviceProxy = new Service();

            //init UsernameToken, password is the reverted 
            //string of username, the same logic in AuthenticateToken
            //  of ServiceUsernameTokenManager class.
            UsernameToken token = new UsernameToken("admin", "nimda", 
                PasswordOption.SendPlainText);

            // Set the token onto the proxy
            serviceProxy.SetClientCredential(token);

            // Set the ClientPolicy onto the proxy
            serviceProxy.SetPolicy("ClientPolicy");

            //invoke the HelloMyFriend web service method
            try
            {
                Console.WriteLine(serviceProxy.HelloMyFriend());
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.Message);
            }
        }
    }
}

Now try to run TestWebService. If you change the password to "nimda1" you will get an exception. You can open OutputTrace.webinfo to see the SOAP message. You might think that the username and password should not be seen. There are several way to secure messages, for example, using SSL (point to point) or WSE3.0 (end to end).

History

  • 6 July, 2007 -- Original version posted
  • 12 July, 2007 -- Updated
  • 4 September, 2007 -- Article edited and moved to the main CodeProject.com article base

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionAwesome Pin
quochoa1522-Feb-23 0:10
quochoa1522-Feb-23 0:10 
QuestionImplement in VS2013 Pin
User 67117821-Apr-15 1:44
User 67117821-Apr-15 1:44 
BugERROR: Failed parsing the policy document Pin
Sharad Shrestha29-Sep-14 0:44
Sharad Shrestha29-Sep-14 0:44 
GeneralRe: ERROR: Failed parsing the policy document Pin
Member 1153229930-Jun-15 4:59
Member 1153229930-Jun-15 4:59 
QuestionAn error was discovered processing the <wsse:Security> header Pin
cwcwilson15-Jul-14 22:01
cwcwilson15-Jul-14 22:01 
QuestionNullReferenceException Pin
1011001025-Nov-12 11:32
1011001025-Nov-12 11:32 
AnswerRe: NullReferenceException Pin
1011001026-Nov-12 4:32
1011001026-Nov-12 4:32 
QuestionGREAT ARTICLE!!! Pin
bburton12-Jun-12 10:53
bburton12-Jun-12 10:53 
QuestionHaving Problem Pin
Alireza_136228-Apr-12 22:55
Alireza_136228-Apr-12 22:55 
QuestionHaving Problem Pin
Alireza_136228-Apr-12 22:52
Alireza_136228-Apr-12 22:52 
QuestionWS-Addressing problem Pin
shinichison13-Apr-12 1:03
shinichison13-Apr-12 1:03 
GeneralMy vote of 5 Pin
shinichison13-Apr-12 1:00
shinichison13-Apr-12 1:00 
BugImplementing same service for web based application. Pin
ninu.aenju12328-Oct-11 2:27
ninu.aenju12328-Oct-11 2:27 
GeneralHow to ensure the Webservice is secured after Implementing WSE3.0 Pin
manish11.j5-May-11 2:36
manish11.j5-May-11 2:36 
Questionhow to i change the username and password? PinPopular
shital@meenu22-Nov-09 23:49
shital@meenu22-Nov-09 23:49 
AnswerRe: how to i change the username and password? Pin
pophelix2-Oct-10 1:03
pophelix2-Oct-10 1:03 
GeneralRe: how to i change the username and password? Pin
tubularnet20074-Nov-10 10:08
tubularnet20074-Nov-10 10:08 
AnswerRe: how to i change the username and password? Pin
yogeshs15-Dec-10 21:36
yogeshs15-Dec-10 21:36 
Questionhow can change username and password Pin
ali.expo7-Nov-09 19:21
ali.expo7-Nov-09 19:21 
GeneralGreat Article! Pin
OakLines16-Jul-09 8:45
OakLines16-Jul-09 8:45 
GeneralAwesome!!! Pin
extremeg15-Jun-09 1:20
extremeg15-Jun-09 1:20 
Thanks man! You just exemplified the beauty of simplicity. Good article!

...the mind is not a vessel to be filled but a fire to ignited

GeneralGreat Article Pin
TheRealSpanner30-Mar-09 3:29
TheRealSpanner30-Mar-09 3:29 
GeneralUsername Token doesn't work when I invoke Web Service in browser Pin
MartaTw13-Jan-09 12:20
MartaTw13-Jan-09 12:20 
GeneralPerfect! Pin
Member 47139939-Jan-09 2:17
Member 47139939-Jan-09 2:17 
QuestionCan it be consume irrespective of platforms like Windows, Linux, Mac and language like java, Perl, c#, vb.Net and others? Pin
Viral Pandya26-Nov-08 19:33
Viral Pandya26-Nov-08 19:33 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.