C# – Correlation failed in net.core / asp.net identity / openid connect

asp.net-coreazure-active-directoryc

I getting this error when a Azure AD user login (I able to get the userĀ“s claims after), im using a combination of OpenIdConnect, with asp.net Identity core over net.core 2.0

An unhandled exception occurred while processing the request.
Exception: Correlation failed.
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler+d__12.MoveNext()

The trace:

Exception: Correlation failed.
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler+d__12.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
System.Runtime.CompilerServices.TaskAwaiter.GetResult()
Microsoft.AspNetCore.Authentication.AuthenticationMiddleware+d__6.MoveNext()
System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware+d__7.MoveNext()

Correlation Failed

Here is my Startup.cs:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using BPT.PC.IdentityServer.Data;
using BPT.PC.IdentityServer.IdentityStore;
using BPT.PC.IdentityServer.Models;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;

namespace BPT.PC.IdentityServer.Web
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddIdentity<User, Role>()
                .AddUserStore<UserStore>()
                .AddRoleStore<RoleStore>()
                .AddDefaultTokenProviders();

            services.AddMemoryCache();
            services.AddDistributedMemoryCache();
            services.AddDbContext<IdentityServerDb>(options => options.UseSqlServer(Configuration.GetConnectionString("IdentityServerDb")));

            services.AddMvc();
            services.AddAuthentication(auth =>
            {
                auth.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                auth.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                auth.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
            })
            .AddCookie()
            .AddOpenIdConnect("AzureAD", opts =>
            {
                Configuration.GetSection("OpenIdConnect").Bind(opts);
                opts.RemoteAuthenticationTimeout = TimeSpan.FromSeconds(120);
                opts.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;

                opts.CorrelationCookie = new Microsoft.AspNetCore.Http.CookieBuilder
                {
                    HttpOnly = false,
                    SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None,
                    SecurePolicy = Microsoft.AspNetCore.Http.CookieSecurePolicy.None,
                    Expiration = TimeSpan.FromMinutes(10)
                };

                opts.Events = new OpenIdConnectEvents()
                {
                    OnRedirectToIdentityProvider = OnRedirectToIdentityProvider,
                    OnRemoteFailure = OnRemoteFailure,
                    OnAuthorizationCodeReceived = OnAuthorizationCodeReceived
                };
                //opts.Events = new OpenIdConnectEvents
                //{
                //    OnAuthorizationCodeReceived = ctx =>
                //    {
                //        return Task.CompletedTask;
                //    }
                //};
            });

            //services.ConfigureApplicationCookie(options =>
            //{
            //    // Cookie settings
            //    options.Cookie.HttpOnly = true;
            //    options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
            //    options.SlidingExpiration = true;
            //});
        }

        private Task OnAuthorizationCodeReceived(AuthorizationCodeReceivedContext arg)
        {
            return Task.FromResult(0);
        }

        private Task OnRemoteFailure(RemoteFailureContext arg)
        {
            return Task.FromResult(0);
        }

        private Task OnRedirectToIdentityProvider(RedirectContext arg)
        {
            return Task.FromResult(0);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseBrowserLink();
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles();
            app.UseAuthentication();

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "default",
                    template: "{controller=Account}/{action=Login}/{id?}");
            });
        }
    }
}

My appsettings.json:

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  },

  "ConnectionStrings": {
    "IdentityServerDb": "Server=localhost;Database=IdentityServer;Trusted_Connection=True;MultipleActiveResultSets=true"
  },

  "OpenIdConnect": {
    "ClientId": "xxxxx",
    "Authority": "https://login.microsoftonline.com/xxxxx/",
    "PostLogoutRedirectUri": "/Account/SignoutOidc",
    "CallbackPath": "/Account/SigninOidc",
    "UseTokenLifetime": true,
    "RequireHttpsMetadata": false,
    //"ResponseType": "code id_token",
    "ClientSecret": "xxx",
    "Resource": "https://graph.microsoft.com/"
  }
}

And the implementation:

[HttpGet]
public IActionResult CorpLogin()
{
  var authProperties = _signInManager
                .ConfigureExternalAuthenticationProperties("AzureAD",
     Url.Action("SigninOidc", "Account", null, Request.Scheme));

   return Challenge(authProperties, "AzureAD");
}

[HttpPost]
public IActionResult SigninOidc([FromForm]object data)
{
//this never runs
   return Ok();
}

Best Answer

If you're using Chrome against localhost, you may have run into a change in Chrome cookie-handling behaviour.

To verify, navigate to chrome://flags/ and change "Cookies without SameSite must be secure" to "Disabled".

If that change fixes the issue, and you want to fix it permanently (i.e. not rely on the chrome flags fix), this thinktecture post talks about the underlying issue and some fixes that you'll need for old iOS safari versions.

Related Topic