Customizing Authentication and Authorization in WSE Using AzMan

SE uses security tokens internally to represent security claims from Web service methods. The security tokens let WSE authenticate the user, validate the password, and check whether the user has sufficient rights to execute the desired Web service method. The Web services security implementation includes authentication as well as authorization. This article discusses how to use custom authentication methods in WSE using an example that authenticates incoming SOAP messages and then authorizes the consumption of a particular service using AzMan, (Windows’ Authorization Manager) with custom principals. As an example, the article uses a telecom dealer application, which lets prospective telecommunications dealers activate postpaid customers and manage their accounts.

WSE Authentication
When you configure WSE 2.0 in your Web service project, clients of that Web service will create two Web service proxy classes named according to your project, such as MyWebService1 and MyWebService1WSE. The second proxy class, derived from the WSE Microsoft.Web.Services2.WebServicesClientProtocol class, contains support for adding WSE Security tokens used to authenticate clients. Client programs must add a security token?for example, a Username token?to the Tokens collection of the RequestSoapContext of the Web service instance implemented with WSE. The Username token isn’t the only possible security token; there are other security tokens?and you can also create a custom security token by creating a custom SecurityTokenManager class. But a security token of some type is required.

When a client application sends a Username security token, a UsernameTokenManager handles the authentication. The TokenManager authenticates the Username token (which contains a username and password) against the Windows principals stored in an Active Directory (AD) database by default. But you can authenticate a Username token against any other user database by extending the SecurityTokenManager class to handle your specific security token.

For example, suppose you need to create a custom security token manager to authenticate security tokens against the credentials of agents in a SQL Server database. The following code snippet shows how a client program might send a Username token:

   using System;   using System.Collections;   using Microsoft.Web.Services2;   using Microsoft.Web.Services2.Security.Tokens;      namespace TestWebService   {      public class TestWseWsClient      {         public TestWseWsClient()         {         }            public static void Main(String[] args)         {            UsernameToken userTok = null;            string agentId = "AGT0001";            string authCode = "6ndBsNS";                     DATPostpaid.DATWebServiceWse wse = new                DATPostpaid.DATWebServiceWse();            userTok = new UsernameToken(agentKey,authCode,                PasswordOption.SendPlainText);            wse.RequestSoapContext.Security.Tokens.Add(               userTok);            Console.WriteLine("Returned Message: " +                 wse. AccountAdjustment("92468387","50"));            Console.ReadLine();         }      }    }

The TestWseWsClient class shown above creates a UsernameToken instance, specifies the username as agentKey, the password as authCode, and sends the password using the SendPlainText option. It then adds that token to the RequestSoapContext’s Tokens collection.

To make a call to a Web method named WelcomeAuthenticateAgent () of a WSE-implemented Web service, the client sends the SOAP message containing the UsernameToken to the Web service endpoint. There, the server must determine whether the user specified in the UsernameToken can be authenticated. In this scenario, that requires some custom code to authenticate the token using a specific database.

Whenever the Client program invokes a Web service with the WSE-enabled proxy class, WSE deserializes the incoming SOAP message and then calls the VerifyToken method for each UsernameToken it contains. The VerifyToken method calls the AuthenticateToken method to get the correct password for the specified user. If you do not extend UserNameTokenManager by overriding the AuthenticateToken method then the default UserNameTokenManager.AuthenticateToken method verifies the username and password in the SOAP message by calling the LogonUser Win32 function.

Listing 1 shows an example that overrides the default AuthenticateToken method:

?
Figure 1. The WSE 2.0 Settings Dialog: To reach this dialog, right click on your WSE Web service project and select “WSE Settings 2.0” from the menu.
?
Figure 2. SecurityToken Manager Definition: The dialog contains the fields needed to define the token.

Notice that the AgentAuthentication class in Listing 1 overrides the AuthenticateToken method and makes a call to an AuthenticateAgentData method to validate the username and password contained in the token. AuthenticateAgentData is a data access method that runs a stored procedure to verify the agentId and authCode values. If the agent can be authenticated, the AuthenticateToken method returns the password which can then be compared against the password specified in the UsernameToken. If they’re the same, the application processes the body of the SOAP message by transferring control to the Web method called AccountAdjustment. Otherwise, the application rejects the SOAP message by raising a Microsoft.Web.Services2.Security.SecurityFault exception, which contains the message: “The security token could not be authenticated or authorized.”

For WSE to use the custom code, you need to define the UserNameTokenManager class and the UserNameToken it should use in the WSE 2.0 property sheet. To do that, right click on your WSE Web service project and select the “WSE Settings 2.0” item. You’ll see the dialog shown in Figure 1. Switch to the Security tab and you’ll see a list of Security Token Managers (the list will be empty initially).

Click on the Add button underneath the list. A dialog box will appear containing three text boxes, as shown in Figure 2.

Fill in the text fields as shown in Table 1 below:

Field Example Value Description
Type DATWebService.Security.AgentAuthentication, DATWebService The strong name of the class that extends UserNameTokenManager, the Web service class that authenticates using this token manager.
Namespace http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd WSE generates a proxy using this XSD schema.
QName wsse:UsernameToken The type of security token used in your SecurityTokenManager class. The sample code uses a UserNameToken.

WSE Authorization
Before digging into Web service Authorization part of Web services, I must tell you about an incredible and simplified Application Authorization Model called AzMan (Authorization Manager) available on the Windows platform. AzMan ships with the Windows Server 2003 family but you can use it even if you’re running Windows XP or Windows Server 2000 by downloading it from this URL.

?
Figure 3. The AzMan Administration GUI: The administration application provides a hierarchical view of Groups, Roles, and other AzMan entities, is an MMC snap-in that you can try out by running AZMAN.MSC or by adding the Authorization Manager snap-in to your MMC console of choice.

AzMan goes far beyond the Private Object Security API of the ACL (Access Control List) model in Windows NT Server 4.0., but AzMan is a set of COM-based interfaces for managing and verifying client requests to perform application operations. AzMan provides a Microsoft Management Console (MMC) snap-in to simplify managing user roles and permissions. I’ve found AzMan to be the solution for managing role-based security in Windows applications.

AzMan has two parts: a runtime and an administrative UI. The runtime, provided by AZROLES.DLL, exposes a set of COM interfaces used by applications that employ role-based security. The administration UI is an MMC snap-in that you can try out by running AZMAN.MSC or by adding the Authorization Manager snap-in to your MMC console of choice.

AzMan Administration
The first thing you’ll notice when you run the AzMan admin tool shown in Figure 3 is that it’s much more complex than that offered by COM+. You no longer have only roles and role assignments; you can also assign low-level operations to tasks, which you can then assign to roles. A task is a collection of operations; you might need series of operations to complete one concrete task. Tasks can include other tasks, and roles can include other roles. This hierarchical approach helps you manage the seemingly unrestrained set of roles needed in today’s multifaceted applications.

For the example code, you’ll first setup an AzMan store (file) as a part of Active Directory or in a simple XML file. Create an XML file called Agent.xml, and then create a new application in that file called DealerManagementApplication.

Here’s an example of an AzMan XML store?built as described in the remainder of this article.

                                       d1445203-a9ae-4270-a48d-1ff486f51d4e                     e80e81cd-558d-4367-b7d6-2d460b4bab70                                 3bf97741-cb1f-4f8b-a894-b2cb3f4f16c4                              1                                   27d4a28b-6e19-4bb4-87f9-7c2a3e67f76f                     31d2b06d-59d8-43a8-ac6a-39739159f4a8                              
?
Figure 4. Defining Application Operations: Using this dialog, you can add a list of named items and operation numbers to AzMan. Later, you map these names to individual tasks.
6567d978-fcfc-4c70-9035-5bee1912e454
2
?
Figure 5. Mapping Operations: It’s best to create one-to-one task-to-operation mappings to simplify maintenance and improve task reusability.

Next, add all the operations individually for which you need role-based security, giving each a name and a unique integer that represents the operation number as shown in Figure 4.

Note that in naming operations, I’ve encoded the names with the prefix “op.” This is simply to avoid naming collisions later when creating tasks and roles since these names must be unique. The next step is to define a set of tasks that map to these low-level operations. As a best practice, Microsoft recommends that you create tasks that each map to a single operation. Such mappings simplify maintainance and improve task reusability?letting you assign tasks to multiple Roles. So, define a task called tsk_AccountAdjustment and add the operation op_AccountAdjustment defined in the previous step and shown in Figure 4. Figure 5 shows the task-mapping step.

?
Figure 6. Creating an Application Group: Provide a name, description, and the Group type for your new Application Group.

You can also add an authorization script to a task defined as a BizRule. A BizRule is a script added to a task when you plan to apply a known constraint to it. For example, suppose you need to set a maximum adjustment amount of $100. When a caller invokes this task the task treats the parameter value passed as an adjustment amount, which it compares to the maximum value defined here in a script that becomes a BizRule. You can write authorization scripts in either VBScript or JavaScript. Authorization scripts can use information that is available only at runtime, such as “time of day” or “dollar amount requested” to make an authentication decision. Next, you define an Application Group that holds information about the users and groups who will use this application. An Application Group can be either a Basic type or an LDAP Query. This example uses the Basic type because it’s not going to assign Windows domain users or groups in this Application Group?it uses a Custom Principal rather than the Windows principal. For this example, create an Application Group as shown in Figure 6.

You don’t have to add members in this Application Group because this example uses the custom principal from the code; however, if you’re planning to use AzMan with your domain users then you would add domain users or group members in this dialog.

?
Figure 7. Associating Tasks with Roles: The dialog provides a modifiable list of tasks and lower-level roles that you can associate with roles.

The next step is to create roles for the application and assign tasks to those roles as shown in Figure 7.

Define a “PremiumDealer” role in role definition dialog shown above and then add tasks to it so that you can assign roles to users or groups of users who need to perform these tasks. Note that you can chain roles. For example, you can add a role to another higher-level role, which is an extremely convenient feature of AzMan’s role-based architecture.

Finally, you assign roles to users or groups. To do this, select the Role Assignments folder (see Figure 3) and choose the Assign Roles action. Note that a role doesn’t actually become active in an application until you’ve added it to this folder. In other words, you first add the “PremiumDealer” role in this folder and then you can assign Application Groups or Windows users and groups to this role. This example assigns the Application Group “DATApplicationsGrp” created earlier to the PremiumDealer role.

Adding AzMan Runtime Access Checks
Having completed the AzMan administration tasks, you can begin implementing access checks in code. First you need to add some keys in your project’s web.config file that map to the AzMan store, the appropriate Application Group, Application Name, and Operation Name as follows:

                  

You also need to write a wrapper class for AzMan where you implement the CheckAccess method and all support methods. The class shown in Listing 2 provides an example.

In the AzMan class shown in Listing 2, the CheckAccess method authenticates a caller for a particular operation in a defined application. First you call the OpenApplication(appName) method that in turn initializes the AzMan store by calling AzManStore.Initialize(0, @azmanFPath,null) method. Next, it opens the application from the store by calling the AzManStore.OpenApplication(ApplicationName,null) method. After that you can create a client by calling the method InitializeClientContextFromStringSid(sid, 1, null). Note that the call initializes the client context from a SID (Security Identifier) and sets the second parameter to 1, which turns off checking the SID against the Windows User Store?but will still match the custom SID (Custom Principal) that you assigned to the Application Group programmatically. The constant in that call is AZ_CLIENT_CONTEXT_SKIP_GROUP and its value is set to 1 in azroles.h.

You can then pass this client context to the CCHandle.AccessCheck(“tsk”,Scopes,Operations,null,null,null,null,null) Azman method, which is quite handy. The AzMan COM component can be used from all COM-enabled languages, including scripting languages such as JavaScript or VBScript. Of course, that also means you often have to deal with Variant data types. The sample code uses an object[] array (which in .NET covers the Variant type) and converts that to an integer type later on.

Now take a look at the Web method that a consumer program would call. If you have an application that stores the principals in a SQL database and you have an AzMan store against which you want to run access checks, you first need to map your principals to the Custom SIDs stored in the database so that you can later retrieve those SIDs to perform operations.

When creating custom SIDs you must establish a SID design for your application. For example, you might have S-1-9-AppInstanceGUID-UserGuid, where 9 is the resource manager subauthority and AppInstanceGUID and UserGUID are each broken into four sub authorities. You can also use S-1-9-AppInstanceGuid-UserRID, where UserRID represents a unique number for the user in the scope of the application instance. For more information see the AzMan documentation.

For example: In this application S-1-9-1-AGT0001 represents the first Dealer agent.

   private string GetAgentId()   {      string agentId = null;      if (RequestSoapContext.Current != null &    RequestSoapContext.Current.Security.Tokens.Count > 0)      {       foreach (UsernameToken tok in           RequestSoapContext.Current.Security.Tokens)        {      agentId = tok.Username;       }      }      else       {        agentId = null;      }      return agentId;   }   

The preceding code first authenticates the token sent by the caller program against the database as described earlier during the authentication portion of this article. After authentication succeeds, and the caller is authenticated then you can use the agentId to retrieve the custom SID from the database.

   [WebMethod]    public string AccountAdjustment(string telephoneNumber, string amount)   {      Azman azInstance = new Azman();      bool accessResult;       string adjustedAmount = "0";      string sid = GetAgentSid(GetAgentId());                  try       {         string operationName = "AccountAdjustment";         accessResult = azInstance.CheckAcces(operationName,sid);                                 if(accessResult)         {         //Code to Adjust amount against the account of telephoneNumber         adjustedAmount = amount;         }         else         {         throw new Exception("AuthorizationAccessDenied: You are not authorized to perform this operation!");         }      }      catch(Exception ex)      {         throw ex;      }      return adjustedAmount;   }

You can add custom SIDs to either roles or Application Groups. When the application loads you can add all the custom SIDs that correspond to agent IDs to the Application Group by calling the AddSidToGroup(sid,applicationGrp) method. Later, you can check access through the AzMan API as discussed above.

So, you’ve seen how to perform SoapHeader authentication using a custom UserNameTokenManager and then perform authorization through AzMan by using a custom principal. By the way, the username tokens described in this article are encrypted even though I didn’t discuss that aspect. You should encrypt and decrypt tokens using RSA or some other encryption algorithm or use a secure protocol such as HTTPS as Microsoft recommends.

Share the Post:
Share on facebook
Share on twitter
Share on linkedin

Overview

Recent Articles: