Archive

Archive for February, 2010

Building a claims aware ASP.NET web application

February 23rd, 2010 No comments

 

This post is a follow up to my previous post about Building a claims aware WCF Service using Windows Identity Foundation. This time, I’ll lay out the steps needed to secure access to a ASP.NET web application using Windows Identity Foundation.

1) Go to the STS application and add the following code to Default.aspx:

protected void Page_PreRender(object sender, EventArgs e)
        {
            string action = Request.QueryString[WSFederationConstants.Parameters.Action];

            try
            {
                if (action == WSFederationConstants.Actions.SignIn)
                {
                    // Process signin request.
                    SignInRequestMessage requestMessage = (SignInRequestMessage)WSFederationMessage.CreateFromUri(Request.Url);
                    if (User != null && User.Identity != null && User.Identity.IsAuthenticated)
                    {
                        SecurityTokenService sts = new AppSecurityTokenService(AppSecurityTokenServiceConfiguration.Current);
                        SignInResponseMessage responseMessage = FederatedPassiveSecurityTokenServiceOperations.ProcessSignInRequest(requestMessage, User, sts);
                        FederatedPassiveSecurityTokenServiceOperations.ProcessSignInResponse(responseMessage, Response);
                    }
                    else
                    {
                        throw new UnauthorizedAccessException();
                    }
                }
                else if (action == WSFederationConstants.Actions.SignOut)
                {
                    // Process signout request.
                    SignOutRequestMessage requestMessage = (SignOutRequestMessage)WSFederationMessage.CreateFromUri(Request.Url);
                    FederatedPassiveSecurityTokenServiceOperations.ProcessSignOutRequest(requestMessage, User, requestMessage.Reply, Response);
                }
                else
                {
                    throw new InvalidOperationException(
                        String.Format(CultureInfo.InvariantCulture,
                                       "The action '{0}' (Request.QueryString['{1}']) is unexpected. Expected actions are: '{2}' or '{3}'.",
                                       String.IsNullOrEmpty(action) ? "<EMPTY>" : action,
                                       WSFederationConstants.Parameters.Action,
                                       WSFederationConstants.Actions.SignIn,
                                       WSFederationConstants.Actions.SignOut));
                }
            }
            catch (Exception exception)
            {
                throw new Exception("An unexpected error occurred when processing the request. See inner exception for details.", exception);
            }
        }

This is just a copy of the boliler plate code that is auto generated by Visual Studio when you create an ASP.NET Security Token Service Web Site.

The two most interesting method calls in the above code is the call to FederatedPassiveSecurityTokenServiceOperations.ProcessSignInRequest(…) and FederatedPassiveSecurityTokenServiceOperations.ProcessSignInResponse(..).

What ProcessSignInRequest(..) does is creating a RST (RequestSecurityToken) based on the requestMessage and a ClaimsPrincipal based on the IPrincipal passed in. It then calls the SecurityTokenService.Issue(…) method passing in the RST and the ClaimsPrincipal and returns a SignInResponseMessage based on the RSTR (RequestSecurityTokenResponse) returned by the SecurityTokenService.Issue method.

ProcessSignInResponse(…) simply writes the content of the the SignInResponseMessage to the HttpResponse output stream.

2) Add a Login.aspx page with a single button. In the button click event, add the following code to simulate a successful login

void buttonLogin_Click(object sender, EventArgs e)
       {
           if (Request.QueryString["ReturnUrl"] != null)
           {
               FormsAuthentication.RedirectFromLoginPage("Tore Senneseth", false);
           }
           else
           {
               FormsAuthentication.SetAuthCookie("Tore Senneseth", false);
               Response.Redirect("default.aspx");
           }

       }

 

3) Open the web.config file and change the authentication from Windows to Forms and deny all unauthenticated users access.

<authentication mode="Forms">
      <forms loginUrl="Login.aspx" protection="All" timeout="30" name=".ASPXAUTH" path="/" 
             requireSSL="false" slidingExpiration="true" defaultUrl="default.aspx" 
             cookieless="UseDeviceProfile" enableCrossAppRedirects="false"/>
    </authentication>
    <!-- Deny Anonymous users. -->
    <authorization>
      <deny users="?"/>
    </authorization>

4) Switch back to the main web application, SecureWebApplication. (Where our claims aware WCF Service is implemented) and open the web.config-file

Change authentication from Windows to None and deny anonymous access.

<authentication mode="None"/>
    <authorization>
      <deny users="?"/>
    </authorization>

6) Add Windows Identity Foundation httpModules to configuration/system.web/httpModules

<httpModules>
      <add name="ClaimsPrincipalHttpModule" 
           type="Microsoft.IdentityModel.Web.ClaimsPrincipalHttpModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      <add name="WSFederationAuthenticationModule" 
           type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      <add name="SessionAuthenticationModule" 
           type="Microsoft.IdentityModel.Web.SessionAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
    </httpModules>

7) Add Windows Identity Foundation modules to configuration/system.webServer/modules

<modules>
      <add name="ClaimsPrincipalHttpModule" 
           type="Microsoft.IdentityModel.Web.ClaimsPrincipalHttpModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
           preCondition="managedHandler"/>
      <add name="WSFederationAuthenticationModule"
           type="Microsoft.IdentityModel.Web.WSFederationAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
           preCondition="managedHandler"/>
      <add name="SessionAuthenticationModule" 
           type="Microsoft.IdentityModel.Web.SessionAuthenticationModule, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" 
           preCondition="managedHandler"/>
    </modules>

We’ve added 3 modules, ClaimsPrincipalHttpModule, SessionAuthenticationModule and WSFederationAuthenticationModule.

What does all of these modules do? For a good explaination, take a look at this blog post by Dominic Baier. Also, you’ll find a detailed explaination about the WSFederationAuthenticationModule in the MSDN documentation here.

8 ) Modify the microsoft.identityModel/service/ element by adding a serviceCertificate element. Here, we’ll need to specify the certificate used by the STS to encrypt the security token so that the Relying party (our web site) is able to decrypt the security token once it is received. In the attached sample project, the certificate being used has the subject name STSTestCert.

<service>
      <serviceCertificate>
        <certificateReference findValue="0E2A9EB75F1AFC321790407FA4B130E0E4E223E2" storeLocation="LocalMachine" storeName="My" x509FindType="FindByThumbprint"/>
      </serviceCertificate>
    </service>

9) Add the uri of your application to the audienceUris element. 

<audienceUris>
        <add value="http://localhost:25546"/>
        <add value="http://localhost:25546/MyService.svc"/>
      </audienceUris>

The audienceUris section specifies a list of valid audience URIs for incoming SAML tokens. 
When the client sends an RST (RequestSecurityToken) to the STS, it usually includes an AppliesTo setting that indicates who the token should be issued to. If present, the STS will use this information to populate the SAML token audience uri setting. When the client receives the issued SAML token, the audience URI setting in the SAML token must match one of the audience URIs specified in the audienceUris list. If the audience Uri setting in the SAML token is not present in the audienceUris list, the token is refused. If you want to disable this behavior, you can set the mode property to “Never”, <audienceUris mode=”Never”/>.

10) To verify that your site is working, set the relying party (SecureWebSite), as the startup project of the solution and Default.aspx as start page, add an asp:Label to Default.aspx and add the following code to the Page_Load event handler.

this.labelUserName.Text = ((IClaimsPrincipal)Thread.CurrentPrincipal).Identity.Name;

Categories: Windows Identity Foundation Tags:

Building a claims aware WCF Service using Windows Identity Foundation

February 18th, 2010 3 comments

Windows Identity Foundation (WIF) just shipped, so I decided to give it a go and see how easy it was to get started.
There’s an easy way to get started building an STS and claims aware WCF service, and there’s a hard way. The easy way is to just use the templates provided by Visual Studio. The hard way is to do everything manually. I decided to do it the hard way because of two reasons;
1: I wanted to learn the details, and 2: If I could get it working, I would WIF-enable existing projects (in my case, Silverlight projects).

So here’s the steps I went through in order to get a my basic claims aware WCF up and running:

1) In Visual Studio, create a new Web Application, let’s call it SecureWebApplication

2) Create an STS application (Security Token Service) by adding a new web application to the solution. Let’s call it SecureWebApplication_STS

3) Add a reference to Microsoft.IdentityModel.dll, System.IdentityModel.dll and System.ServiceModel.dll to the STS application.
Note! You may need to set Copy Local = true for Microsoft.IdentityModel.dll before the STS app can be called.

4) In the STS application, add the following classes: Common, AppSecurityTokenService, CertificateUtil and AppSecurityTokenServiceConfiguration. (Get implementation of classes from the web site you get when creating an ASP.NET Security Token Service Web Site. File -> New -> Web Site…).

5) Add a new text file to the STS application. Call it AppSTS.svc.

6) Edit AppSTS.svc so it contains the following line;

<%@ ServiceHost Language="C#" 
                Debug="true" 
                Factory="Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceHostFactory" 
                Service="SecureWebApplication_STS.AppSecurityTokenServiceConfiguration"  %>


This will enable our STS to be hosted by a service host in our web application.

7) In the STS application, edit the web.config file and add configuration settings that specifies how the STS should encrypt and sign issued security tokens;

<appSettings>
    <add key="IssuerName" value="SigninSTS"/>
    <add key="SigningCertificateName" value="CN=STSTestCert"/>
    <add key="EncryptingCertificateName" value="CN=STSTestCert"/>
  </appSettings>


Note! The STSTestCert is a X509Certificate installed as part of the Windows Identity Foundation SDK. Feel free to use a different certificate, but this walkthrough assumes that STSTestCert is used.

8 ) In the STS application, edit the web.config file and add the WCF service configuration for the STS service. 

<system.serviceModel>
    <services>
      <service name="Microsoft.IdentityModel.Protocols.WSTrust.WSTrustServiceContract" 
               behaviorConfiguration="ServiceBehavior">
        <endpoint address="IWSTrust13" binding="ws2007HttpBinding" 
                  contract="Microsoft.IdentityModel.Protocols.WSTrust.IWSTrust13SyncContract" 
                  bindingConfiguration="ws2007HttpBindingConfiguration"/>
        <host>
          <baseAddresses>
            <add baseAddress="http://localhost:14499/AppSTS.svc"/>
          </baseAddresses>
        </host>
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
      </service>
    </services>
    <bindings>
      <ws2007HttpBinding>
        <binding name="ws2007HttpBindingConfiguration">
          <security mode="Message">
            <message establishSecurityContext="false"/>
          </security>
        </binding>
      </ws2007HttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="ServiceBehavior">
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="false"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>


9) We should now have a working STS that can be used by WCF Services. Let’s go back to your main web application and add a claims aware WCF Service.

10) Go to the main web application, SecureWebApplication, and add a reference to Microsoft.IdentityModel.dll and System.ServiceModel.dll.

Note! You may need to set Copy Local = true property for Microsoft.IdentityModel.dll before executing the app.

11) Add a Silverlight-enabled WCF Service, or just a plain WCF Service. I’ll be using a SL-enabled WCF Service in this walkthrough. Lets call the service MyService.scv.

12) Once you have added the WCF service, open the web.config-file

13) In the web.config-file, remove the default customBinding configuration added by Visual Studio and specify the service to use ws2007FederationHttpBinding instead.

Optionally, you can remove the <serviceHostingEnvironment aspNetCompatibilityEnabled=”True” /> element.

<services>
      <service behaviorConfiguration="SecureWebApplication.MyServiceBehavior"
               name="SecureWebApplication.MyService">
        <endpoint address="" binding="ws2007FederationHttpBinding"
                  bindingConfiguration="MyServiceBindingConfiguration"
                  contract="SecureWebApplication.MyService"/>
        <endpoint address="mex" binding="mexHttpBinding"
                  contract="IMetadataExchange"/>
      </service>
 </services>


14) Before we can configure the service behavior, we need to register a WCF extension for Windows Identity Foundation which allows federation.

In system.serviceModel, add an the following section

<extensions>
      <behaviorExtensions>
        <!-- This behavior extension will enable the service host to be Claims aware -->
        <add name="federatedServiceHostConfiguration" 
             type="Microsoft.IdentityModel.Configuration.ConfigureServiceHostBehaviorExtensionElement, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>
      </behaviorExtensions>
 </extensions>


15) Now we’re ready to define the service behavior. Add the following behavior to system.serviceModel/behaviors/serviceBehaviors. The service certificate we specify in the behavior is the certificate used by the STS to encrypt the security token (the certificate configured in step 7)

<behavior name="SecureWebApplication.MyServiceBehavior">
          <federatedServiceHostConfiguration/>
          <serviceMetadata httpGetEnabled="true"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <serviceCredentials>
            <serviceCertificate findValue="0E2A9EB75F1AFC321790407FA4B130E0E4E223E2" 
                                storeLocation="LocalMachine" storeName="My" 
                                x509FindType="FindByThumbprint"/>
          </serviceCredentials>
 </behavior>


16) Finally, we need to configure our ws2007FederationBinding, which is the binding used by out STS.

Add the following binding to system.serviceModel/bindings

<ws2007FederationHttpBinding>
        <binding name="MyServiceBindingConfiguration">
          <security mode="Message">
            <message>
              <issuerMetadata address="http://localhost:14499/AppSTS.svc/mex" />
            </message>
          </security>
        </binding>
      </ws2007FederationHttpBinding>


17) Our next steps involves configuring our WCF Service to use Windows Identity Foundation, so we’ll just stay in the web.config-file.

18) The first step is to register a new WIF config section so that we can specify our WIF configuration in the web.config-file. Add the following line to configuration/configSections in the web.config-file

<section name="microsoft.identityModel" 
             type="Microsoft.IdentityModel.Configuration.MicrosoftIdentityModelSection, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35"/>


19) Now we’re ready to WIF-enable our WCF service. We do that by specifying the content of the microsoft.identityModel section as follows:

<microsoft.identityModel>
  <service>
    <audienceUris>
      <add value="http://localhost:14488/MyService.svc"/>
    </audienceUris>
    <federatedAuthentication>
      <wsFederation passiveRedirectEnabled="true" issuer="http://localhost:14499" realm="http://localhost:14488" requireHttps="false"/>
      <cookieHandler requireSsl="false"/>
    </federatedAuthentication>
    <issuerNameRegistry type="Microsoft.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, Microsoft.IdentityModel, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35">
      <trustedIssuers>
        <add thumbprint="0E2A9EB75F1AFC321790407FA4B130E0E4E223E2" name="http://localhost:14499"/>
      </trustedIssuers>
    </issuerNameRegistry>
  </service>
</microsoft.identityModel>


20) We now have a working claims aware WCF Service. To test it, you can create a new test web application in the same solution and add a service reference to the service.

Windows Identity Foundation – a good starting point

February 5th, 2010 No comments

Getting started with WIF may be a bit confusing, so a good starting point at understanding the basics of WIF is the MSDN section A Guide to Claims-Based Identity and Access ControlIn particular, the topic Claims-Based Single Sign-On for the Web explains the basic plumbing of WIF and roles of the config sections in the web.config-file.

Activity Start Interval in the Parallel Activity

February 3rd, 2010 No comments

A Data Flow Parallel Activity has a setting called “Activity Start Interval”. Activity Start Interval specifies the time in seconds elapsed between the start of each activity. For example, if the value is 0.2, the activities will start at a 0.2 seconds interval. The reason you’d want to use this setting is that on some systems, you may experience connection pool problems when the activities are started to rapidly. As a result, the Data Flow activities will fail to start and you will get the following error message in the log.
Timeout expired.  The timeout period elapsed prior to obtaining a connection from the pool.  This may have occurred because all pooled connections were in use and max pool size was reached.

Setting the Activity Start Interval to a higher value will fix this problem.