Search code examples
c#asp.net-coreazure-active-directory

Correlation failed in net.core / asp.net identity / openid connect


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();
}

Solution

  • I've finally found the solution, I´ll post here just in case somebody have a similar problem.

    Looks like the principal problem was that my redirect URI was the same that the CallBackPath:

    "CallbackPath": "/Account/SigninOidc"

    var authProperties = _signInManager .ConfigureExternalAuthenticationProperties("AzureAD", Url.Action("SigninOidc", "Account", null, Request.Scheme));

    Well, here is my corrected 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 BPT.PC.IdentityServer.Web.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.Http;
    using Microsoft.AspNetCore.Identity;
    using Microsoft.EntityFrameworkCore;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.IdentityModel.Protocols.OpenIdConnect;
    
    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", "AzureAD", options =>
                    {
                        Configuration.GetSection("AzureAD").Bind(options); ;
                        options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
                        options.RemoteAuthenticationTimeout = TimeSpan.FromSeconds(120);
                        options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                        options.RequireHttpsMetadata = false;
                        options.SaveTokens = true;
                    });
    
                services.AddSingleton(Configuration.GetSection("OpenIdConnectProviderConfiguration").Get<OpenIdConnectProviderConfiguration>());
    
            }
            // 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?}");
                });
            }
        }
    }
    

    And the finally implementation:

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

    The appsettings.json is the same.