Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / .NET

Introduction to Claims-Based Authentication and Authorization in .NET – how to build Active STS

4.33/5 (2 votes)
26 Jun 2015CPOL6 min read 9.9K  
How to build Active STS

blog_Authorization

In the previous article, basic information about authentication and authorization process on .NET has been presented. Security Token Service is an integral part of claim based approach. As we remember from the previous article, STS is responsible for handling user’s requests and creating tokens; it can also work in two modes: active and passive. In this article, we focus on Active STS, as we will only try to build sample STS. All technical aspects connected with security, e.g., configuration, certificates, encryption, signature or CRL will be described in detail in the next posts.

Active STS

Active STS is a special type of web service, which is based on WS-Trust protocol. In our scenario, STS will be built in WCF (Windows Communication Foundation) and hosted using http protocol on IIS Server. Communication based on WCF provides authentication and authorization mechanisms and offers a TransportWithMessageCredential security mode that is designed to achieve “confidentiality and integrity” requirements. When this security mode is configured, the transport security is used to provide confidentiality and integrity for the transmitted messages to perform the service authentication. In WCF, with TransportWithMessageCredential, security mode authentication mechanism can be based on:

  • Windows Credentials
  • User Name Credentials
  • Certificate Credentials
  • Issued Token Credentials

How to Build Active STS

In this article, we try to build STS with User Name Credentials authentication and authorization mechanism. We prepare sample STS service that will be hosted on IIS and Relying Party application (a WinForm application that relies on claims for authentication), which will be an STS client. We try to accomplish scenario, which has been presented in the following diagram. [caption id="attachment_5710" align="aligncenter" width="139"].

Figure 1. STS -Reying Party Application

Figure 1. STS -Reying Party Application[/caption]
  1. The client initializes and sends (via application) authentication request to the STS. Request contains client's credentials required to authenticate the client.
  2. The STS validates the client's credentials.
  3. The STS issues a security token to the client. If the client's credentials are successfully validated, the STS issues a security token (such as a SAML token). The token contains claims, which represent user’s identity.

At first, we build and host STS projects.

Active Security Token Service Project

STS implementation is based on the following .NET classes:

These classes allow to prepare STS “template” project, which can be extended depending on our own requirements, e.g., authentication mechanism which can use a user/password mechanism or certificates. In the figure below the sequence of events, in which an STS issues a security token, has been presented. [caption id="attachment_5711" align="aligncenter" width="605"]

Figure 2. The sequence of events in which an STS issues a security token

Figure 2. The sequence of events in which an STS issues a security token[/caption]

Sample STS Implementation

In our solution, authentication mechanism will be used, which is based on a user’s name/password method. To implement your own authentication mechanism, we should override ValidateToken method from class, which inherits UserNameSecurityTokenHandler. On the listing below, a sample validation mechanism, which validates user’s credentials and generates user’s claims, has been presented:

Listing 1: Sample implementation ValidateToken method

C#
public override ReadOnlyCollection ValidateToken(SecurityToken token)
        {
            var userToken = token as UserNameSecurityToken;
            if(userToken == null)
            {
                throw new FaultException("Incorrect user name or password.");
            }
            ValidateUserCredentials(userToken);

            return new ReadOnlyCollection(ExtractClaims(userToken));
        }

Object userToken (class UserNameSercuirtyToken) contains information about a user’s name and a user’s password that are used to verify user’s identity. If user’s identity is successfully verified, the ValidateToken method returns claims identity collection; generally, this collection contains one ClaimsIdentity object. To create claim identity objects, we can use an exemplary code:

Listing 2: Sample implementation “extract claims” mechanism

C#
private IList ExtractClaims(UserNameSecurityToken userToken)
        {
            const string Label = "UserIdentity";
            const string AdminUser = "Admin";
            const string SuperAdmin = "SuperAdmin";
            var email =  string.Format("{0}@{1}.org",userToken.UserName.ToLower(),Label.ToLower());
            ClaimsIdentity outgoingIdentity = new ClaimsIdentity(Label);
            outgoingIdentity.Label = Label;
            outgoingIdentity.AddClaim(new Claim(ClaimTypes.Name,userToken.UserName));
            outgoingIdentity.AddClaim(new Claim(ClaimTypes.NameIdentifier, "1"));
            outgoingIdentity.AddClaim(new Claim(ClaimTypes.Country, "PL"));
            outgoingIdentity.AddClaim(new Claim(ClaimTypes.Email, email));
            if (userToken.UserName.Equals(SuperAdmin))
            {
                outgoingIdentity.AddClaim(new Claim(ClaimTypes.Role, SuperAdmin));
                outgoingIdentity.AddClaim(new Claim(ClaimTypes.Role, AdminUser));
            }
            if(userToken.UserName.Equals(AdminUser))
            {
                outgoingIdentity.AddClaim(new Claim(ClaimTypes.Role, AdminUser));
            }
            outgoingIdentity.AddClaim(new Claim(ClaimTypes.Role, "User"));
            var identityList = new List { outgoingIdentity };

            return identityList;
        }

The active STS is a special type of web service with ws2007HttpBinding binding. In the listing below, a sample configuration has been presented:

Listing 3: Sample STS configuration (with ws2007HttpBinding binding)

XML
<system.servicemodel>
    <bindings>
      <ws2007httpbinding>
        <binding name="STS-WS2007HttpBinding">
          <security mode="TransportWithMessageCredential">
            <transport clientcredentialtype="None">
            <message algorithmsuite="Default" clientcredentialtype="UserName" establishsecuritycontext="false">
          </message></transport></security>
        </binding>
      </ws2007httpbinding>
    </bindings>
  </system.servicemodel>

The class STSFactory (which inherits ServiceHostFactory) loads binding settings from configuration file. The current STS doesn’t encrypt the received tokens; issued tokens are only signed by STS via STS certificate. The STS uses certificate from LocalMachine->My store, by certificate thumbprint (configuration file SigningCertificateThumbprint key).

Relying Party Application

A Relying Party is an application or service that relies on claims for authentication. In our example, RP application is a simple Win Form application. The application sends an issue request to STS and receives Security Token, which is used to authenticate and authorize user. The application uses the IWSTrustContract contract for sending WS-Trust messages to an STS. WS-Trust is a WS-* specification and OASIS standard that provides extensions to WS-Security, specifically dealing with the issuing, renewing, and validating security tokens. To connect with STS, we don’t have to add WCF proxy to it, instead we can create communication channel with ws2007HttpBinding binding (listing 5). The WSTrustChannelFactory uses information about STS endpoint (address, binding) from a configuration file (endpoint name = "WS2007HttpBinding_IWSTrust13Sync") –typical WCF service configuration.

Listing 4: Sample RP configuration (with ws2007HttpBinding binding)

XML
  <system.servicemodel>
    <behaviors>
      <endpointbehaviors>
        <behavior name="STS">
        </behavior>
      </endpointbehaviors>
      <servicebehaviors>
        <behavior name="STS">
          <servicedebug includeexceptiondetailinfaults="true">
        </servicedebug></behavior>
      </servicebehaviors>
    </behaviors>
    <bindings>
      <ws2007httpbinding>
        <binding name="WS2007HttpBinding_IWSTrust13Sync">
          <security mode="TransportWithMessageCredential">
            <transport clientcredentialtype="None">
            <message algorithmsuite="Default" clientcredentialtype="UserName" 
                       establishsecuritycontext="false">
          </message></transport></security>
        </binding>
      </ws2007httpbinding>
    </bindings>
    <client>
      <endpoint address="https://localhost/STS/STSService.svc" 
           behaviorconfiguration="STS" binding="ws2007HttpBinding" 
           bindingconfiguration="WS2007HttpBinding_IWSTrust13Sync" 
           contract="System.ServiceModel.Security.IWSTrustChannelContract" 
           name="WS2007HttpBinding_IWSTrust13Sync">
    </endpoint></client>
  </system.servicemodel>

The WS-Trust Issue method is responsible for sending request to STS and returning SecurityToken that represents the token issued by the STS. The RequestSecurityToken object contains the parameters and properties used to request a security token from STS.

Listing 5: Send request to STS and receive issued token

C#
var rst = new RequestSecurityToken(RequestTypes.Issue);
   rst.AppliesTo = new EndpointReference("https://RelyingParty/*");
   rst.KeyType = KeyTypes.Bearer;

   using (var trustChannelFactory = new WSTrustChannelFactory("WS2007HttpBinding_IWSTrust13Sync"))
            {
                trustChannelFactory.Credentials.UserName.UserName = userName;
                trustChannelFactory.Credentials.UserName.Password = userPassword;

                var channel = (WSTrustChannel)trustChannelFactory.CreateChannel();
                try
                {
                    _authToken = channel.Issue(rst);
                }
                catch (MessageSecurityException ex)
                {
                    channel.Abort();
                    throw new SecurityTokenException(ex.InnerException.Message,ex);
                }
                _userIdentity = CreateUserIdentityFromSecurityToken(_authToken);
            }

RP application uses received SecuirtyToken to create the ClaimsIdentity object. Below, a sample code which allows to create ClaimsIdenity object from SecurityToken.

Listing 6: Create UserIdentity from issued token

C#
private ClaimsPrincipal CreateUserIdentityFromSecurityToken(SecurityToken token)
        {
            var genericToken = token as GenericXmlSecurityToken;
            var handlers = FederatedAuthentication.FederationConfiguration.
                           IdentityConfiguration.SecurityTokenHandlerCollectionManager.
                           SecurityTokenHandlerCollections.First();
            var cToken = handlers.ReadToken(new XmlTextReader(new StringReader
                         (genericToken.TokenXml.OuterXml)));
            var identity = handlers.ValidateToken(cToken).First();
            var userIdenity = new ClaimsPrincipal(identity);
            return userIdenity;
        }

How to Run and Test a Sample Solution

To run the sample Active STS implementation, you should execute the following steps:

  1. Get sample source code from Git repository (source code)
  2. Build STS.sln solution
  3. Install STS certificate (misc\STS.p12 certificate, password:demo)
    1. Run mmc console
    2. Add “Certificates” snap in (choose Computer account and Local computer)
    3. Choose Personal Certificates storage
    4. Import STS.p12 file (All Tasks->Import)
    5. Add full permissions to the STS private key for IIS_IUSRS (Select STS certificate, choose All Tasks->Manage Private Keys; [caption id="attachment_5712" align="aligncenter" width="201"]

      Figure 3. STS key permission

      Figure 3. STS key permission[/caption]
  4. Host STSWebHost project on IIS (use https)
  5. Run Relying Party Application
    1. Login to STS using a user’s name/password credential (Current STS uses authentication mechanism, which verifies if user’s name corresponds to password, e.g., user’s name: user, password: user. If a user’s name is Admin, user is assigned to the User and Admin roles; if a user’s name is SuperAdmin, user is assigned to the User, Admin and SuperAdmin roles. [caption id="attachment_5713" align="aligncenter" width="464"]

      Figure 4. Relying Party Application

      Figure 4. Relying Party Application[/caption]

Summary

In this article, technical approach to authentication and authorization process based on claims via Active STS has been presented. [caption id="attachment_5719" align="aligncenter" width="341"]

WCF Service with STS

Figure 5. WCF Service with STS[/caption]

The next article will describe technical approach to WCF service with authentication and authorization mechanism based on claims. We try to extend our solution to accomplish the following scenario:

  1. The client (via application) sends a request message to the service. The request message contains received token.
  2. The service validates the security token and processes the request. To validate a token connection between service and STS is not necessary – issuer validation is based on PKI (this mechanism will be further described in another article)
  3. (Optional) The service initializes and sends a response message to the client.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)