static void

WS-Federation Configuration

Support for WS-Federation is based on .net 4.5 Claims Security. Claims-based identity means an application (Relying Party, RP) uses a separate service (Security Token Service, STS / Identity Provider, IdP) for security.

See Claims Security for basics on ClaimsPrincipal and WS-Federation for architecture, tooling.

For WCF/SOAP there's a ws2007FederationHttpBinding (message/issuerMetadata @address)

All you need may be configuration. MSDN

Config Sections

<configuration>
  <configSections>
    <section name="system.identityModel"
            type="System.IdentityModel.Configuration.SystemIdentityModelSection,
            System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
    <section name="system.identityModel.services"
            type="System.IdentityModel.Services.Configuration.SystemIdentityModelServicesSection,
            System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
  </configSections>

system.web - NO authentication

<system.web>
  <compilation debug="true" targetFramework="4.5" />
  <httpRuntime targetFramework="4.5" requestValidationMode="4.5" />
  <!-- no authentication, because the FAM/SAM modules below do it -->
  <authentication mode="None"/>
  <!--<authentication mode="Forms">
    <forms loginUrl="~/Account/Login" />
  </authentication>-->
</system.web>

But you still need to protect resources. In MVC just add the [Authorize] filter. In WebForms, add a module and hook it in the web.config:

public class MustBeAuthenticatedModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.PostAuthenticateRequest += context_BeginRequest;
    }
 
    void context_BeginRequest(object sender, EventArgs e)
    {
        var context = HttpContext.Current;
        if (context.Request.Path.StartsWith("/bundles", StringComparison.OrdinalIgnoreCase)) return;
        if (context.User == null || context.User.Identity == null || !context.User.Identity.IsAuthenticated)
        {
            context.Response.StatusCode = 401;
            context.Response.End();
        }
    }

You may get an error "A potentially dangerous Request.Form value was detected from the client (wresult=" because the SAML xml is posted from the STS.
Set the requestValidationMode attribute to 4.5
  <httpRuntime targetFramework="4.5" requestValidationMode="4.5" />

system.webServer - the FAM/SAM modules

<system.webServer>
  <modules>
    <!-- "SAM" - manages the cookie -->
    <add name="SessionAuthenticationModule"
        type="System.IdentityModel.Services.SessionAuthenticationModule,
        System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
        preCondition="managedHandler" />
    <!-- "FAM" - waits for the not authorized 401 and 302 redirects to the STS -->
    <add name="WSFederationAuthenticationModule"
        type="System.IdentityModel.Services.WSFederationAuthenticationModule,
        System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"
        preCondition="managedHandler" />
  </modules>
</system.webServer>

system.identityModel - trust the STS, webfarm support

For developing (eg on EmbeddedSTS) you don't need these settings. Note that certificates expire over time!

If you're using a webfarm, the machine key bit is critical.

<system.identityModel>
  <identityConfiguration>
    <securityTokenHandlers>
      <!-- For WebFarms: session tokens are protected by DPAPI which is machine-specific.
          To spread across a webfarm, use the Machine Key -->
      <remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler,
            System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089" />
      <add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler,
          System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=B77A5C561934E089">
        <sessionTokenRequirement lifetime="00:30:00" />
      </add>
      <securityTokenHandlerConfiguration>
        <!-- issuerNameRegistry and audienceUris used to be directly under identityConfiguration-->
 
        <!-- Check this application trusts the STS.
        There's only one built in issuer name registry.-->
        <issuerNameRegistry
          type="System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry,
          System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
          <trustedIssuers>
            <!-- Checks the token is signed with an X509 certificate with this thumbprint
            Gives the claim issuer the name "LocalSTS" -->
            <add thumbprint="12E..." name="LocalSTS" />
          </trustedIssuers>
        </issuerNameRegistry>
 
        <!-- OPTIONAL, probably not needed. Check the signing certificate trust chain and whether it's been revoked -->
        <!--<certificateValidation
          certificateValidationMode="PeerOrChainTrust"
          revocationMode="NoCheck" />-->
 
        <!-- The realm name (URI of Relying Party) is embedded into the token.
          To ensure the token is for this RP, use audienceUris
          Otherwise client can send token from shared STS for application A to application B (re-purposing attack). -->
        <audienceUris>
          <clear/>
          <!-- same as realm, below -->
          <add value="http://ThisApplication/"/>
        </audienceUris>
      </securityTokenHandlerConfiguration>
    </securityTokenHandlers>
  </identityConfiguration>
</system.identityModel>

system.identityModel.services - link the STS

Like forms authentication, this points to the STS which could be a login page (401 becomes a 302 to the STS). ADFS can be configured with rules for transformations for each relying party (eg send group membership as claim).

Using embeddedSTS here.

<system.identityModel.services>
  <federationConfiguration>
    <!-- equivalent to <authentication mode="Forms"><forms loginUrl="~/Account/Login" />
    NB: requireHttps="false" for development. In prod, you should use SSL.
    issuer = the STS to redirect to e.g. https://xx/issue/wsfed
    realm = a URI for this application (known to the STS).
          Can also be where the STS redirects back to (otherwise set reply=""). -->
    <wsFederation passiveRedirectEnabled="true"
                  issuer="http://EmbeddedSts"
                  realm="http://ThisApplication/"
                  requireHttps="false" />
    <cookieHandler requireSsl="false" />
  </federationConfiguration>
</system.identityModel.services>