Secure ASP.Net 4.5 with ADFS 2.0

 

Introduction

* ASP.Net 4.5 web application can be secured with ADFS 2.0
* Authentication configuration is done in Web.config file

Generate Initial Web.config

* Use Visual Studio to generate an initial Web.config by pointing to ADFS Federation Metadata URL.

adfsaspnet1_newsln_1

* Click Change Authentication button:

adfsaspnet1_newsln_auth_1

* Enter ADFS federation metadata URL into On-Premises Authority field.

adfsaspnet1_newsln_auth_2

* Click OK buttons to create the initial web application
* Relevant elements in the initial Web.config file:

<?xml version="1.0" encoding="utf-8"?>
<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>
  <appSettings>
    <add key="ida:FederationMetadataLocation" value="https://sts.my.com/federationmetadata/2007-06/federationmetadata.xml" />
    <add key="ida:Realm" value="https://localhost:44304/" />
    <add key="ida:AudienceUri" value="https://localhost:44304/" />
  </appSettings>
  <system.web>
    <authentication mode="None" />
    <authorization>
      <deny users="?" />
    </authorization>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" requestValidationMode="4.5" />
  </system.web>
  <system.webServer>
    <modules>
      <add name="WSFederationAuthenticationModule" type="System.IdentityModel.Services.WSFederationAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
      <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
    </modules>
  </system.webServer>
  <system.identityModel>
    <identityConfiguration>
      <audienceUris>
        <add value="https://localhost:44304/" />
      </audienceUris>
      <securityTokenHandlers>
        <add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
        <remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      </securityTokenHandlers>
      <certificateValidation certificateValidationMode="None" />
      <issuerNameRegistry type="System.IdentityModel.Tokens.ValidatingIssuerNameRegistry, System.IdentityModel.Tokens.ValidatingIssuerNameRegistry">
        <authority name="urn:my:adfs">
          <keys>
            <add thumbprint="F5ADED8729AF0005FA889CCB913C7B7AEFF96B33" />
          </keys>
          <validIssuers>
            <add name="urn:my:adfs" />
          </validIssuers>
        </authority>
      </issuerNameRegistry>
    </identityConfiguration>
  </system.identityModel>
  <system.identityModel.services>
    <federationConfiguration>
      <cookieHandler requireSsl="true" />
      <wsFederation passiveRedirectEnabled="true" issuer="https://sts.my.com/adfs/ls/" realm="https://localhost:44304/" requireHttps="true" />
    </federationConfiguration>
  </system.identityModel.services>
</configuration>

Find Issuer Name

* You can find the Issuer Name, e.g. urn:my:adfs, from the initial Web.config file (see previous section):

        <authority name="urn:my:adfs">
          ...
          <validIssuers>
            <add name="urn:my:adfs" />
          </validIssuers>
        </authority>

* Alternatively, you can find it from ADFS2’s federationmetadata.xml file:
– Point browser to federation metadata page, e.g.: https://sts.my.com/federationmetadata/2007-06/federationmetadata.xml
– Find <entityID> attribute, which is the issuer name, at the beginning of the XML:

<EntityDescriptor ID="xxxx"
 entityID="urn:my:adfs"

Find Issuer Thumbprint

* You can find the thumbprint from the initial Web.config file (see previous section):

        <authority name="urn:my:adfs">
          <keys>
            <add thumbprint="F5ADED8729AF0005FA889CCB913C7B7AEFF96B33" />
          </keys>

* Alternatively, you can find thumbprint value from ADFS2’s federationmetadata.xml file:
– Point browser to federation metadata page, e.g.: https://sts.my.com/federationmetadata/2007-06/federationmetadata.xml
– Find <KeyDescriptor use=”signing”> element:

<KeyDescriptor use="signing">
    <KeyInfo xmlns="http://www.w3.org/2000/09/xmldsig#">
        <X509Data>
            <X509Certificate>xxxxxxxxxxxx</X509Certificate>
        </X509Data>
    </KeyInfo>
</KeyDescriptor>

– Copy and paste base64 characters between X509Certificate element into a temp file with .cer file name ending, e.g. mysts_signing.cer
– Double click the .cer temp file to open the certificate
– Find thumbprint value

adfsaspnet1_thumbprint_1

* You still need to prepare the thumbprint value:
– Copy thumbprint value (do NOT copy the first empty space, it contains hidden characters!)
– Remove white spaces
– Upper case whole string, e.g. final value is: F5ADED8729AF0005FA889CCB913C7B7AEFF96B33
* You’ll get “Error ID4175 and ConfigurationBasedIssuerNameRegistry” error if you have the wrong thumbprint value.
– See this post for more details.

Final Web.config

* Initial Web.config generated by Visual Studio is only a starting point.
* Need to modify, e.g.:
– Replace https://localhost:44306/ with actual website URL, e.g. https://asp.my.com
* Following is a working example.

Example Web.config

<?xml version="1.0" encoding="utf-8"?>
<!--
  For more information on how to configure your ASP.NET application, please visit
  http://go.microsoft.com/fwlink/?LinkId=169433
  -->
<configuration>
  <configSections>
 
    <!-- config wif -->
    <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>
 
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="PreserveLoginUrl" value="true" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
 
    <!-- config wif -->
    <add key="ida:FederationMetadataLocation" value="https://sts.my.com/federationmetadata/2007-06/federationmetadata.xml" />
    <add key="ida:Realm" value="https://myappc.my.com/myapp/" />
    <add key="ida:AudienceUri" value="https://myappc.my.com/myapp/" />
  </appSettings>
  <system.web>
    <customErrors mode="Off"/>
    <compilation targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" requestValidationMode="4.5" />
    <pages>
      <namespaces>
        <add namespace="System.Web.Helpers" />
        <add namespace="System.Web.Mvc" />
        <add namespace="System.Web.Mvc.Ajax" />
        <add namespace="System.Web.Mvc.Html" />
        <add namespace="System.Web.Routing" />
        <add namespace="System.Web.WebPages" />
      </namespaces>
    </pages>
 
    <!-- config wif -->
    <authorization>
      <deny users="?" />
    </authorization>
  </system.web>
  <system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <handlers>
      <remove name="ExtensionlessUrlHandler-Integrated-4.0" />
      <remove name="OPTIONSVerbHandler" />
      <remove name="TRACEVerbHandler" />
      <add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="*" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
    </handlers>
    <modules>
 
    <!-- config wif -->
      <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" preCondition="managedHandler" />
      <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>
 
  <!-- config wif -->
  <system.identityModel>
    <identityConfiguration>
      <audienceUris>
        <add value="https://myappc.my.com/myapp/" />
      </audienceUris>
      <issuerNameRegistry type="System.IdentityModel.Tokens.ConfigurationBasedIssuerNameRegistry, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <trustedIssuers>
          <!-- name is IdP entityID which can be found in federationmetadata.xml file -->
          <add thumbprint="xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" name="urn:my:adfs" />
        </trustedIssuers>
      </issuerNameRegistry>
      <certificateValidation certificateValidationMode="None" />
      <securityTokenHandlers>
        <add type="System.IdentityModel.Services.Tokens.MachineKeySessionSecurityTokenHandler, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
        <remove type="System.IdentityModel.Tokens.SessionSecurityTokenHandler, System.IdentityModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
      </securityTokenHandlers>
    </identityConfiguration>
  </system.identityModel>
 
  <!-- config wif -->
  <system.identityModel.services>
    <federationConfiguration>
      <cookieHandler requireSsl="true" />
      <wsFederation passiveRedirectEnabled="true" issuer="https://sts.my.com/adfs/ls/" realm="https://myappc.my.com/myapp/" requireHttps="true" />
    </federationConfiguration>
  </system.identityModel.services>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Helpers" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Mvc" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-5.1.0.0" newVersion="5.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.Optimization" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-1.1.0.0" newVersion="1.1.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="System.Web.WebPages" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-3.0.0.0" newVersion="3.0.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="WebGrease" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="1.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
</configuration>

Example Code

* Example to print out all cliam types and values:

        protected void Page_Load(object sender, EventArgs e)
        {
            var Identity = (ClaimsIdentity)User.Identity;
            if (!Identity.IsAuthenticated)
            {
                log.ErrorFormat("User {0} not authenticated!", GetUserName(Identity));
            }
            else
            {
                log.InfoFormat("User {0} authenticated!", GetUserName(Identity));
            }
 
            var claims = Identity.Claims;
            foreach (Claim c in claims)
            {
                log.InfoFormat("Got claim {0} with value {1}", c.Type, c.Value);
            }
        }

Create Relying Party

* Login ADFS server
* Open ADFS management console
* Go to: AD FS 2.0 > Trust Relationships > Replying Party Trusts
* Right click and select Add Relying Party Trust…
* Click Start on Welcome page:
* Select: Enter data about the relying party manually
* Enter:
– Display name: Test ADFS2
* Select: AD FS 2.0 profile
* Skip for now on Configure Certificate screen
* Select: Enable support for the WS-Federation Passive protocol
– Relying party WS-Federation Passive protocol URL: https://myappc.my.com/
* Click OK on Configure Identifiers
* On Choose Issuance screen, select Permit all users to access this replying party
* Review settings on Ready to Add Trust screen
* Click Next to add the relying party

Add Claim Rules

* Login ADFS server
* Open ADFS management console
* Go to: AD FS 2.0 > Trust Relationships > Replying Party Trusts
* Select relying party, e.g. Test ADFS2
* Click Edit Claim Rules…
* Click Add Rule…
* Enter:
– Claim rule name: Name ID
– Attribute store: Active Directory
– LDAP Attribute: SAM-Account-Name
– Outgoing Cliam Type: Name ID
* Click OK twice
* Add additional rules, e.g. E-mail Address

Deploy ASP.Net 4.5 to IIS 7.5

* Start IIS Manager
* Add new application pool
* Add new application
* Enable anonymous authentication
* Enable SSL
* Setup Web.config file
* Restart Default Web Site

Test

* Point browser to https://myappc.my.com/Default.aspx
* Check log file for claims, e.g.

User jimmy authenticated!
Got claim http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier with value jimmy
Got claim http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationmethod with value http://schemas.microsoft.com/ws/2008/06/identity/authenticationmethod/windows
Got claim http://schemas.microsoft.com/ws/2008/06/identity/claims/authenticationinstant with value 20xx-xx-02T15:40:14.391Z
This entry was posted in adfs, asp.net and tagged , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *


*

This site uses Akismet to reduce spam. Learn how your comment data is processed.