«^»
8.3. Modifying a server to check a username and a password

In order to use the WSE, you will need to make some changes to the web.config file that is used by the Web Service. This file is stored in the same directory as the other files of the project (e.g., Service.asmx.cs). It contains an XML document. You will need to add the following to its configuration element:

0256: <configSections>
0257:     <section 
0258:         name="microsoft.web.services"
0259:         type="Microsoft.Web.Services.Configuration.WebServicesConfiguration,
0260:                  Microsoft.Web.Services, Version=2.0.0.0, Culture=neutral, 
0261:                  PublicKeyToken=31bf3856ad364e35" />
0262: </configSections>
0263: <microsoft.web.services>
0264:     <security>
0265:         <securityTokenManager
0266:             qname="wsse:UsernameToken"
0267:             xmlns:wsse="http://schemas.xmlsoap.org/ws/2002/12/secext"
0268:             type="ServerUsername.MyUsernameTokenManager, ServerUsername" />
0269:     </security>
0270: </microsoft.web.services>
and the following needs to be added to the system.web element:
0271: <webServices>
0272:     <soapExtensionTypes>
0273:         <add 
0274:             type="Microsoft.Web.Services.WebServicesExtension, 
0275:                      Microsoft.Web.Services, Version=2.0.0.0, Culture=neutral, 
0276:                      PublicKeyToken=31bf3856ad364e35" 
0277:             priority="1" 
0278:             group="0"/>
0279:     </soapExtensionTypes>
0280: </webServices>
Note: the text of each of the two type elements given above needs to be typed on one line: above, they are presented on several lines in order to help you read them.

One of the changes to the web.config file arranges for it to include a securityTokenManager element. The contents of this element ensure that ServerUsername.MyUsernameTokenManager's AuthenticateToken method gets used.

Now, whenever the Web Service is contacted, automatically behind the scenes, the WSE SOAP Extension will authenticate the usernames/passwords of any UsernameToken elements of the header of the SOAP message.

But we have to ensure that an incoming SOAP message actually has one UsernameToken element. In the following code for a Web Service providing a ToFahrenheit method, this is done by a subsidiary method called iGetUsernameToken:

0281: using PasswordOption       = Microsoft.Web.Services.Security.PasswordOption;
0282: using RequestSoapContext   = Microsoft.Web.Services.RequestSoapContext;
0283: using SecurityToken        = Microsoft.Web.Services.Security.SecurityToken;
0284: using SoapContext          = Microsoft.Web.Services.SoapContext;
0285: using SoapException        = System.Web.Services.Protocols.SoapException;
0286: using UsernameToken        = Microsoft.Web.Services.Security.UsernameToken;
0287: using UsernameTokenManager = Microsoft.Web.Services.Security.Tokens.UsernameTokenManager;
0288: using System;
0289: using System.Collections;
0290: using System.ComponentModel;
0291: using System.Data;
0292: using System.Diagnostics;
0293: using System.Web;
0294: using System.Web.Services;
0295: namespace ServerUsername
0296: {
0297:     [WebService(Namespace="http://www.a.com/webservices/",
0298:          Description="This Web Service provides temperature conversion services.")]
0299:     public class Service1 : System.Web.Services.WebService
0300:     {
0301:         ...  <<< as lines 28 to 60 on page 3 of this document >>>
0302:         [WebMethod(Description="This converts from Centigrade to Fahrenheit.")]
0303:         public double ToFahrenheit(double pCentigrade)
0304:         {
0305:             UsernameToken tUsernameToken = iGetUsernameToken();
0306:             return 32 + pCentigrade*9/5;
0307:         }
0308:         private static UsernameToken iGetUsernameToken()
0309:         {
0310:             SoapContext tSoapContext = RequestSoapContext.Current;
0311:             if (tSoapContext==null) 
0312:             {
0313:                 throw new Exception("Only SOAP requests are permitted");
0314:             }
0315:             if (tSoapContext.Security.Tokens.Count!=1)
0316:             {
0317:                 throw new SoapException("There must be one security token", 
0318:                     SoapException.ClientFaultCode);
0319:             }
0320:             foreach (SecurityToken tSecurityToken in tSoapContext.Security.Tokens)
0321:             {
0322:                 if (tSecurityToken is UsernameToken)
0323:                 {
0324:                     UsernameToken tUsernameToken = (UsernameToken)tSecurityToken;
0325:                     if (tUsernameToken.PasswordOption==PasswordOption.SendHashed)
0326:                     {
0327:                         return tUsernameToken;
0328:                     }
0329:                     else
0330:                     {
0331:                         throw new SoapException("The PasswordOption has the wrong value", 
0332:                             SoapException.ClientFaultCode);
0333:                     }
0334:                 }
0335:                 else
0336:                 {
0337:                     throw new SoapException("A UsernameToken was expected", 
0338:                         SoapException.ClientFaultCode);
0339:                 }
0340:             }
0341:             throw new SoapException("A SecurityToken has not been found",
0342:                 SoapException.ClientFaultCode);
0343:         }
0344:     }
0345:     ...  <<< as lines 241 to 255 on page 11 of this document >>>
0346: }

You will see that this code uses some types from the Microsoft.Web.Services namespace (and from some of its subnamespaces). These namespaces are a part of the WSE. In order to get Visual Studio.NET to find these types, you will need to go to the Add Reference option of its Project menu, and then use this option to add a reference to Microsoft.Web.Services.dll. This file is one of those that will have been created by installing the WSE.

Although the above code is checking that there is a UsernameToken element, it does not prevent replay attacks: it is being lazy in that it does not include code to check the Nonce (tUsernameToken.Nonce) and the timestamp value (tUsernameToken.Created) of the incoming SOAP message.