title: SAML2 httpcontext is already authenticated
body: <p>I'm trying to build an ASPNet SAML2 orientated WebApp and i'm facing issues. I'm using the ITFoxtec library to handle all the process but i'm struggling when a user is trying to reconnect on the webapp.
  The scenario that causes the issue is the following:</p>
  <ol>
  <li>Launch WebApp on a linux serveur on port 49500</li>
  <li>Connect to the WebApp thanks to IE on a Windows session through the route &quot;Auth/Login&quot;</li>
  <li>Token is generated and accessible</li>
  <li>Try to regenerate once more the token through the route &quot;Auth/Login&quot;</li>
  </ol>
  <p>And at this point, the WebApp trigger the following exception:</p>
  <blockquote>
  <p>System.InvalidOperationException: There already exist an Authenticated user.
  at ITfoxtec.Identity.Saml2.MvcCore.Saml2ResponseExtensions.CreateSession(Saml2AuthnResponse saml2AuthnResponse, HttpContext httpContext, Nullable`1 lifetime, Boolean isPersistent, Func`2 claimsTransform) in C:\XXXXX\ITfoxtec.Identity.Saml2-0c0cef05050d633f000be9399dc6a5b1a009a6da\src\ITfoxtec.Identity.Saml2.MvcCore\Extensions\Saml2ResponseExtensions.cs:line 24</p>
  </blockquote>
  <p>After digging a little bit inside the ITFoxted code, the error is triggered by the <code>HttpContext.User.Identity.IsAuthenticated</code> being set to <code>True</code> when it's run on Linux. I've also seen that on the first iteration, <code>HttpContext.User.Identity.AuthenticationType</code> is set to <code>null</code> and on the second iteration it is set to <code>Federation</code>.
  I have done multiple test on multiple environnment and i've end up with the following notes:</p>
  <ol>
  <li>Running the WebApp on Windows with a local pfx works (even with multiple refresh)</li>
  <li>Running the WebApp on Windows the AuthenticationType is never set to Federation</li>
  </ol>
  <p>Listed below are the code for launching the <code>WebApp</code> and the <code>AuthController</code>.</p>
  <pre><code>[AllowAnonymous]
  [EnableCors(&quot;SAML_CORS&quot;)]
  [Route(&quot;Auth&quot;)]
  public class SamlController : Controller
  {
      const string relayStateReturnUrl = &quot;ReturnUrl&quot;;
      const string ClaimMatricule = &quot;matricule&quot;;
      const string WebappsJwtRequest = &quot;getJwt=true&quot;;
      const string WebappsJwtFailure = &quot;jwt=FAILED&quot;;
      private readonly Saml2Configuration config;
  
      public IConfiguration Configuration { get; private set; }
  
      public SamlController(IConfiguration configuration, IOptions&lt;Saml2Configuration&gt; configAccessor)
      {
          this.Configuration = configuration;
          config = configAccessor.Value;
      }
  
      [Route(&quot;Metadata&quot;)]
      public ObjectResult Metadata()
      {
          X509Certificate2Collection certificates = new X509Certificate2Collection();
          certificates.Import(Configuration[&quot;Saml2:SigningCertificateFile&quot;], null, X509KeyStorageFlags.PersistKeySet);
          var cert = certificates[0];
          string publicSigingKey = Convert.ToBase64String(cert.RawData);
          
          string metadataTemplate = System.IO.File.ReadAllText(Configuration[&quot;Jwt:SpMetadataFilePath&quot;]);
  
          metadataTemplate = metadataTemplate.Replace(&quot;PUBLIC_RSA_SHA1_SIGNING_CERTIFICATE&quot;, publicSigingKey);
          metadataTemplate = metadataTemplate.Replace(&quot;USERNAME_ATTRIBUTE&quot;, Configuration[&quot;Saml2:ClaimIdentifierName&quot;]);
  
          string fullHostName = Dns.GetHostEntry(&quot;&quot;).HostName.ToLower();
          if (!Configuration.GetSection(&quot;CustomIssuer&quot;).GetChildren().Any(item =&gt; item.Key == fullHostName))
          {
              fullHostName = &quot;localhost&quot;;
          }
          metadataTemplate = metadataTemplate.Replace(&quot;ISSUER_TO_REPLACE&quot;, Configuration[&quot;CustomIssuer:&quot; + fullHostName]);
  
          string currentUrl = HttpContext.Request.Host.Value;
          metadataTemplate = metadataTemplate.Replace(&quot;SAML_ASSERTION_CONSUMPTION_ENDPOINT&quot;, $&quot;https://{currentUrl}/Auth/AssertionConsumerService&quot;);
  
          return new ObjectResult(metadataTemplate)
          {
              StatusCode = (int)HttpStatusCode.OK
          };
      }
  
      [Route(&quot;Login&quot;)]
      public object Login(string returnUrl = null)
      {
          var binding = new Saml2RedirectBinding();
          binding.SetRelayStateQuery(new Dictionary&lt;string, string&gt; { { relayStateReturnUrl, returnUrl ?? Url.Content(&quot;~/&quot;) } });
  
          if (returnUrl != null &amp;&amp; returnUrl.Contains(WebappsJwtFailure))
          {
              return View();
          }
  
          if (returnUrl != null &amp;&amp; returnUrl.Contains(WebappsJwtRequest))
          {
              return binding.Bind(new Saml2AuthnRequest(config)
              {
                  ForceAuthn = true,
              }).RedirectLocation.ToString();
          }
          return binding.Bind(new Saml2AuthnRequest(config)
          {
              ForceAuthn = true,
          }).ToActionResult();
  
      }
  
      [Route(&quot;AssertionConsumerService&quot;)]
      public async Task&lt;IActionResult&gt; AssertionConsumerService()
      {
  
          var binding = new Saml2PostBinding();
          var saml2AuthnResponse = new Saml2AuthnResponse(config);
  
          binding.ReadSamlResponse(Request.ToGenericHttpRequest(), saml2AuthnResponse);
  
          var relayStateQuery = binding.GetRelayStateQuery();
          var returnUrl = relayStateQuery.ContainsKey(relayStateReturnUrl) ? relayStateQuery[relayStateReturnUrl] : Url.Content(&quot;~/&quot;);
  
          if (saml2AuthnResponse.Status != Saml2StatusCodes.Success) {
              var failureReason = $&quot;Unsuccessful SAML Response status: {saml2AuthnResponse.Status}&quot;;
              Console.WriteLine(failureReason);
              returnUrl = returnUrl.Contains(WebappsJwtRequest) ? returnUrl.Replace(WebappsJwtRequest, $&quot;jwt=FAILED({failureReason})&quot;) : $&quot;{returnUrl}?jwt=FAILED&quot;;
              return Redirect(returnUrl);
          }
          binding.Unbind(Request.ToGenericHttpRequest(), saml2AuthnResponse);
          var samlContent = await saml2AuthnResponse.CreateSession(HttpContext, claimsTransform: (claimsPrincipal) =&gt; ClaimsTransform.Transform(claimsPrincipal));
          var claims = samlContent.Claims;
          var matricule = claims.Where(c =&gt; c.Type == Configuration[&quot;Saml2:ClaimIdentifierName&quot;]).First().Value;
          var jwt = this.GenerateJWT(matricule);         
  
          if (returnUrl.Contains(WebappsJwtRequest))
          {
              // If this endpoint is used as an API by the proxy SAML web app, then we need to send the JWT token to it
              returnUrl = returnUrl.Replace(WebappsJwtRequest, &quot;jwt=&quot; + jwt);
          }
  
          return Redirect(returnUrl);
      }
  
      [HttpPost(&quot;Logout&quot;)]
      [ValidateAntiForgeryToken]
      public async Task&lt;IActionResult&gt; Logout()
      {
          if (!User.Identity.IsAuthenticated)
          {
              return Redirect(Url.Content(&quot;~/&quot;));
          }
  
          var binding = new Saml2PostBinding();
          var saml2LogoutRequest = await new Saml2LogoutRequest(config, User).DeleteSession(HttpContext);
          return Redirect(&quot;~/&quot;);
      }
  
      private string GenerateJWT(String userMatricule)
      {
          var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration[&quot;Jwt:Key&quot;]));
          var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha512);
  
          string fullHostName = Dns.GetHostEntry(&quot;&quot;).HostName.ToLower();
          if (!Configuration.GetSection(&quot;CustomIssuer&quot;).GetChildren().Any(item =&gt; item.Key == fullHostName))
          {
              fullHostName = &quot;localhost&quot;;
          }
          var token = new JwtSecurityToken(
            Configuration[&quot;CustomIssuer:&quot; + fullHostName],
            Configuration[&quot;CustomIssuer:&quot; + fullHostName],
            new Claim[] { new Claim(ClaimMatricule, userMatricule) },
            expires: DateTime.Now.AddMinutes(Double.Parse(Configuration[&quot;Jwt:ValidityDurationInMinutes&quot;])),
            signingCredentials: credentials
          );
  
          return new JwtSecurityTokenHandler().WriteToken(token);
      }
  }
  </code></pre>
  <p>Below, the launcher:</p>
  <pre><code>public class Program
  {
      public static void Main(string[] args)
      {
          var configuration = new ConfigurationBuilder()
                  .SetBasePath(Directory.GetCurrentDirectory())
                  // custom config file
                  .AddJsonFile(IdentifyJsonConf(), optional: false, reloadOnChange: false)
                  .Build();
          CreateWebHostBuilder(args, configuration).Build().Run();
      }
  
      public static IWebHostBuilder CreateWebHostBuilder(string[] args, IConfiguration conf)
      {
          
          return WebHost.CreateDefaultBuilder(args)
              .UseWebRoot(&quot;wwwroot_saml&quot;)
              .UseKestrel(options =&gt;
              {
                  options.ListenAnyIP(int.Parse(conf[&quot;Port&quot;]), listenOptions =&gt;
                  {
                      listenOptions.UseHttps(conf[&quot;Saml2:SigningCertificateFile&quot;]);
                  });
              })
              .UseConfiguration(conf)
              .UseStartup&lt;Startup&gt;();
      }
  
      private static string IdentifyJsonConf()
      {
          string currentDir = Directory.GetCurrentDirectory();
  
          // On récupère le chemin du fichier de config (Linbux par defaut)
          var confPath = @&quot;xxxxxx&quot;;
          var confFile = &quot;xxxx.json&quot;;
  
          var path = System.Reflection.Assembly.GetExecutingAssembly().Location;
          var dir = Path.GetDirectoryName(path);
          var configFileLinux = new FileInfo(Path.Combine(dir, confFile));
  
          if (!configFileLinux.Exists)
          {
              configFileLinux = new FileInfo(Path.Combine(confPath, confFile));
          }
  
          // On recupere l'OS afin de determiner quel fichier de config utiliser
          OperatingSystem os = Environment.OSVersion;
          PlatformID pid = os.Platform;
          switch (pid)
          {
              // Si on est sur du Windows, alors c'est pour du test
              case PlatformID.Win32NT:
              case PlatformID.Win32S:
              case PlatformID.Win32Windows:
              case PlatformID.WinCE:
                  Console.WriteLine(&quot;Getting xxxx.json config file because running on Windows!&quot;);
  
                  confFile = &quot;xxxxx.json&quot;;
                  var configFileWindows = new FileInfo(Path.Combine(Environment.CurrentDirectory, confFile));
  
                  if (!configFileWindows.Exists)
                  {
                      configFileWindows = new FileInfo(Path.Combine(dir, confFile));
                  }
                  configFileLinux = configFileWindows;
                  break;
              // Si on est sur du Linux
              case PlatformID.Unix:
                  break;
              default:
                  break;
          }
          return configFileLinux.Name;
      }
  
  }
  </code></pre>
  <p>Thanks for the help!</p>