Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

After logging in 'User.Identity.Name' is null, but 'IsAuthenticated' is true when using Sustainsys.Saml2.Mvc integration

I have an ASP.NET MVC 5 application, and need to support SAML 2.0 authentication. I am evaluating Sustainsys.Saml.Mvc. The User.Identity.Name property in my controller is an empty string, while the User.Identity.IsAuthenticated property is true, and can't for the life of me figure it out.

While installing the Sustainsys.Saml2.Mvc NuGet package, I had to do the following things:

  1. Upgrade the .NET framework from 4.5.1 to 4.6.1
  2. Install Sustainsys.Saml2.Mvc v2.2.0
  3. Upgrade the Microsoft.AspNet.Mvc package from 5.2.3 to 5.2.7
  4. Tweaked Web.config settings according to the Sustainsys.Saml2 documentation
  5. Downloaded the .cert and .pfx files from their demo MVC application

Contents of 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=301880
  -->
<configuration>
  <configSections>
    <sectionGroup name="elmah">
      <section name="security" requirePermission="false" type="Elmah.SecuritySectionHandler, Elmah" />
      <section name="errorLog" requirePermission="false" type="Elmah.ErrorLogSectionHandler, Elmah" />
      <section name="errorMail" requirePermission="false" type="Elmah.ErrorMailSectionHandler, Elmah" />
      <section name="errorFilter" requirePermission="false" type="Elmah.ErrorFilterSectionHandler, Elmah" />
    </sectionGroup>
    <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" />
    <section name="sustainsys.saml2" type="Sustainsys.Saml2.Configuration.SustainsysSaml2Section, Sustainsys.Saml2" />
  </configSections>
  <connectionStrings>
    ...
  </connectionStrings>
  <appSettings>
    <add key="webpages:Version" value="3.0.0.0" />
    <add key="webpages:Enabled" value="false" />
    <add key="ClientValidationEnabled" value="true" />
    <add key="UnobtrusiveJavaScriptEnabled" value="true" />
  </appSettings>
  <!--
    For a description of web.config changes see http://go.microsoft.com/fwlink/?LinkId=235367.

    The following attributes can be set on the <httpRuntime> tag.
      <system.Web>
        <httpRuntime targetFramework="4.6.1" />
      </system.Web>
  -->
  <system.web>
    <customErrors mode="Off" />
    <compilation debug="true" targetFramework="4.6.1" />
    <httpRuntime targetFramework="4.6.1" requestValidationMode="2.0" />
    <authentication mode="Forms">
      <forms loginUrl="~/Saml2/SignIn" />
    </authentication>
    <membership defaultProvider="ABC">
      <providers>
        <clear />
        <add name="ABC" type="System.Web.Security.ActiveDirectoryMembershipProvider" connectionStringName="ADConnectionString" attributeMapUsername="sAMAccountName" />
      </providers>
    </membership>
    <httpModules>
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" />
      <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" />
      <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" />
    </httpModules>
  </system.web>
  <system.webServer>
    <handlers>
      <add name="UrlRoutingHandler" type="System.Web.Routing.UrlRoutingHandler, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" path="Authorization/Permissions/*" verb="GET,POST" />
    </handlers>
    <staticContent>
      <remove fileExtension="eot" />
      <remove fileExtension="otf" />
      <remove fileExtension="woff" />
      <remove fileExtension="woff2" />
      <remove fileExtension="ttf" />
      <remove fileExtension="json" />
      <mimeMap fileExtension="eot" mimeType="application/vnd.ms-fontobject" />
      <mimeMap fileExtension="otf" mimeType="application/x-font-opentype" />
      <mimeMap fileExtension="woff" mimeType="application/x-font-woff" />
      <mimeMap fileExtension="woff2" mimeType="application/font-woff2" />
      <mimeMap fileExtension="ttf" mimeType="application/x-font-ttf" />
      <mimeMap fileExtension="json" mimeType="application/json" />
    </staticContent>
    <validation validateIntegratedModeConfiguration="false" />
    <modules>
      <add name="ErrorLog" type="Elmah.ErrorLogModule, Elmah" preCondition="managedHandler" />
      <add name="ErrorMail" type="Elmah.ErrorMailModule, Elmah" preCondition="managedHandler" />
      <add name="ErrorFilter" type="Elmah.ErrorFilterModule, Elmah" preCondition="managedHandler" />
      <add name="SessionAuthenticationModule" type="System.IdentityModel.Services.SessionAuthenticationModule, System.IdentityModel.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
    </modules>
    <httpErrors errorMode="Detailed" />
  </system.webServer>
  <sustainsys.saml2 entityId="http://localhost/MyMvcApp/Saml2" returnUrl="http://localhost/MyMvcApp/">
    <identityProviders>
      <add entityId="https://stubidp.sustainsys.com/Metadata" signOnUrl="https://stubidp.sustainsys.com/" allowUnsolicitedAuthnResponse="true" binding="HttpRedirect">
        <signingCertificate fileName="~/App_Data/stubidp.sustainsys.com.cer" />
      </add>
    </identityProviders>
    <!--<federations>
      <add metadataLocation="http://localhost:52071/Federation" allowUnsolicitedAuthnResponse="true"/>
    </federations>-->
    <serviceCertificates>
      <add fileName="~/App_Data/Sustainsys.Saml2.Tests.pfx" />
    </serviceCertificates>
  </sustainsys.saml2>
  <system.identityModel.services>
    <federationConfiguration>
      <cookieHandler requireSsl="false" />
    </federationConfiguration>
  </system.identityModel.services>
  <runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin.Security" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin.Security.OAuth" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin.Security.Cookies" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Microsoft.Owin" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-3.0.1.0" newVersion="3.0.1.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Newtonsoft.Json" culture="neutral" publicKeyToken="30ad4fe6b2a6aeed" />
        <bindingRedirect oldVersion="0.0.0.0-10.0.0.0" newVersion="10.0.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="WebGrease" publicKeyToken="31bf3856ad364e35" />
        <bindingRedirect oldVersion="0.0.0.0-1.5.2.14234" newVersion="1.5.2.14234" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Autofac" publicKeyToken="17863af14b0044da" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-3.5.0.0" newVersion="3.5.0.0" />
      </dependentAssembly>
      <dependentAssembly>
        <assemblyIdentity name="Oracle.DataAccess" publicKeyToken="89b483f429c47342" culture="neutral" />
        <bindingRedirect oldVersion="0.0.0.0-4.121.2.0" newVersion="4.121.2.0" />
      </dependentAssembly>
      <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.WebPages" 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.2.7.0" newVersion="5.2.7.0" />
      </dependentAssembly>
    </assemblyBinding>
  </runtime>
  <system.net>
    <mailSettings>
      <smtp deliveryMethod="SpecifiedPickupDirectory" from="[email protected]">
        <network host="smtp.mycompany.com" port="25" defaultCredentials="true" />
        <specifiedPickupDirectory pickupDirectoryLocation="..." />
      </smtp>
    </mailSettings>
  </system.net>
  <elmah>
    <!--
        See http://code.google.com/p/elmah/wiki/SecuringErrorLogPages for
        more information on remote access and securing ELMAH.
    -->
    <security allowRemoteAccess="true" />
    <errorLog type="Elmah.MemoryErrorLog, Elmah" size="50" />
  </elmah>
  <location path="elmah.axd" inheritInChildApplications="false">
    <system.web>
      <httpHandlers>
        <add verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" />
      </httpHandlers>
      <!--
        See http://code.google.com/p/elmah/wiki/SecuringErrorLogPages for
        more information on using ASP.NET authorization securing ELMAH.-->
      <authorization>
        <allow users="..." />
        <deny users="*" />
      </authorization>
    </system.web>
    <system.webServer>
      <handlers>
        <add name="ELMAH" verb="POST,GET,HEAD" path="elmah.axd" type="Elmah.ErrorLogPageFactory, Elmah" preCondition="integratedMode" />
      </handlers>
    </system.webServer>
  </location>
</configuration>

I'm currently hosting my application from my laptop:

  • Windows 10 Enterprise
  • .NET framework up to 4.6.1 is installed
  • IIS 10.0.15063.0
  • Application pool settings in IIS:
    • .NET 4.0
    • Allow 32-bit applications

It is using the mock Identity Provider at: https://stubidp.sustainsys.com/

The XML posted to /MyApp/Saml2/Acs:

<saml2p:Response Destination="http://localhost/MyApp/Saml2/Acs" ID="idb4440bf88fba449f8526760d4330dd53" Version="2.0" IssueInstant="2018-12-20T18:21:22Z" InResponseTo="idd9b8948ac5ac4c389bf65072169464ac"
    xmlns:saml2p="urn:oasis:names:tc:SAML:2.0:protocol">
    <saml2:Issuer
        xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion">https://stubidp.sustainsys.com/Metadata
    </saml2:Issuer>
    <Signature
        xmlns="http://www.w3.org/2000/09/xmldsig#">
        <SignedInfo>
            <CanonicalizationMethod Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
            <SignatureMethod Algorithm="http://www.w3.org/2001/04/xmldsig-more#rsa-sha256" />
            <Reference URI="#idb4440bf88fba449f8526760d4330dd53">
                <Transforms>
                    <Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature" />
                    <Transform Algorithm="http://www.w3.org/2001/10/xml-exc-c14n#" />
                </Transforms>
                <DigestMethod Algorithm="http://www.w3.org/2001/04/xmlenc#sha256" />
                <DigestValue>...</DigestValue>
            </Reference>
        </SignedInfo>
        <SignatureValue>...</SignatureValue>
        <KeyInfo>
            <X509Data>
                <X509Certificate>...</X509Certificate>
            </X509Data>
        </KeyInfo>
    </Signature>
    <saml2p:Status>
        <saml2p:StatusCode Value="urn:oasis:names:tc:SAML:2.0:status:Success" />
    </saml2p:Status>
    <saml2:Assertion
        xmlns:saml2="urn:oasis:names:tc:SAML:2.0:assertion" Version="2.0" ID="_d6ffcd18-44ec-45db-bd74-9cf48ea1cfa2" IssueInstant="2018-12-20T18:21:22Z">
        <saml2:Issuer>https://stubidp.sustainsys.com/Metadata</saml2:Issuer>
        <saml2:Subject>
            <saml2:NameID Format="urn:oasis:names:tc:SAML:1.1:nameid-format:unspecified">JohnDoe</saml2:NameID>
            <saml2:SubjectConfirmation Method="urn:oasis:names:tc:SAML:2.0:cm:bearer">
                <saml2:SubjectConfirmationData NotOnOrAfter="2018-12-20T18:23:22Z" InResponseTo="..." Recipient="http://localhost/MyApp/Saml2/Acs" />
            </saml2:SubjectConfirmation>
        </saml2:Subject>
        <saml2:Conditions NotOnOrAfter="2018-12-20T18:23:22Z">
            <saml2:AudienceRestriction>
                <saml2:Audience>http://localhost/MyApp/Saml2</saml2:Audience>
            </saml2:AudienceRestriction>
        </saml2:Conditions>
        <saml2:AuthnStatement AuthnInstant="2018-12-20T18:21:22Z" SessionIndex="42">
            <saml2:AuthnContext>
                <saml2:AuthnContextClassRef>urn:oasis:names:tc:SAML:2.0:ac:classes:unspecified</saml2:AuthnContextClassRef>
            </saml2:AuthnContext>
        </saml2:AuthnStatement>
    </saml2:Assertion>
</saml2p:Response>

I also set the Options.Logger property on the controller, and this is what I'm getting in the debug output:

[Saml2, DEBUG]: Http POST binding extracted message
<saml2p:Response Destination="http://localhost/MyApp/Saml2/Acs" ...>...</saml2p:Response>
[Saml2, DEBUG]: Signature validation passed for Saml Response Microsoft.IdentityModel.Tokens.Saml2.Saml2Id
[Saml2, DEBUG]: Extracted SAML assertion _a5ee9c7f-4ca5-4693-a7f7-301ae5e6d4a6
[Saml2, INFO]: Successfully processed SAML response Microsoft.IdentityModel.Tokens.Saml2.Saml2Id and authenticated JohnDoe

While debugging the application, the User.Identity.Name property in my MVC controller is null. Further inspection of that object:

Screenshot of debug console in Visual Studio

Why is System.Web.Mvc.Controller.User.Identity.Name null after successfully asserting the SAML response?

like image 310
Greg Burghardt Avatar asked Dec 20 '18 18:12

Greg Burghardt


2 Answers

You have properly completed the authentication. And you have a working session authentication cookie.

What you don't have however is a claim that matches the default name claim type. You have the NameIdentifier claim (which is what the stub idp supplies by default). But you don't have a claim with the default NameClaimType

The Name property of a ClaimsIdentity is implemented as

return Claims.FirstOrDefault(c => c.Type == NameClaimType)?.Value;

So to get a value on the Name property you either need to change the NameClaimType (can be done by modifying the created identity in the AcsCommandResultCreated notification) or get your Idp to provide an attribute of type http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name. You can do that by adding an attribute at the bottom of the stubidp form.

like image 55
Anders Abel Avatar answered Nov 12 '22 02:11

Anders Abel


I'm not using Identity server, but same basic issue:

Look at your Claims under User.Identity.Claims and find one that does have your username.

enter image description here

Then wherever you configure the tokenvalidation you have to put the NameClaimType to be the one you saw above:

Then when the middleware validates it it will copy that claim into UserName. I'm assuming this is some default for Windows authentication [sic].

   var tokenValidationParameters = new TokenValidationParameters
   {
            NameClaimType = "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier",
like image 40
Simon_Weaver Avatar answered Nov 12 '22 01:11

Simon_Weaver