14

I'm using IdentityServer 4 to provide authentication and autorisation for my web app, using an external login provider (Microsoft).

This works fine when I run both IdentityServer and my web app locally. However, when I publish the Identityserver project to Azure, it no longer works.

When I connect my locally running web app to the published IdentityServer, after returning from the Microsoft login page, the web app fails with the error 'Correlation failed. unknown location'.

The output from the web app shows:

Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler: 
Warning: '.AspNetCore.Correlation.oidc.xxxxxxxxxxxxxxxxxxx' cookie not found.

Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler: 
Information: Error from RemoteAuthentication: Correlation failed..

However, when I check my browser, a cookie with the exact name '.AspNetCore.Correlation.oidc.xxxxxxxxxxxxxxxxxxx' does exist..

Here's the startup.cs from the web app:

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.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
        services
            .AddTransient<ApiService>();



        services.AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie()
        .AddOpenIdConnect("oidc", options =>
        {
            options.SignInScheme = "Cookies";

            options.Authority = Configuration.GetSection("IdentityServer").GetValue<string>("AuthorityUrl"); 
            //options.RequireHttpsMetadata = true;

            options.ClientId = "mvc";
            options.ClientSecret = "secret";
            options.ResponseType = "code id_token";

            options.SaveTokens = true;
            options.GetClaimsFromUserInfoEndpoint = true;

            options.Scope.Add("api1");
            options.Scope.Add("offline_access");
        });

        services.AddLocalization(options => options.ResourcesPath = "Resources");
        services.Configure<RequestLocalizationOptions>(options =>
        {
            var supportedCultures = new[]
            {
                new CultureInfo("nl-NL"),
                new CultureInfo("en-US")
            };
            options.DefaultRequestCulture = new RequestCulture("nl-NL", "en-US");
            options.SupportedCultures = supportedCultures;
            options.SupportedUICultures = supportedCultures;
        });

        services.AddMvc()
            .AddViewLocalization(LanguageViewLocationExpanderFormat.Suffix)
            .AddDataAnnotationsLocalization();

        services.AddMvc(config =>
        {
            var policy = new AuthorizationPolicyBuilder()
                             .RequireAuthenticatedUser()
                             .Build();
            config.Filters.Add(new AuthorizeFilter(policy));
        });

        services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
    }

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)
    {
        if (env.IsDevelopment())
        {
            app.UseDeveloperExceptionPage();
        }
        else
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }

        app.UseAuthentication();
        app.UseHttpsRedirection();
        app.UseStaticFiles();
        app.UseCookiePolicy();

        app.UseMvc(routes =>
        {
            routes.MapRoute(
               name: "areas",
               template: "{area:exists}/{controller}/{action}/{id?}");

            routes.MapRoute(
                name: "default",
                template: "{controller=Home}/{action=Index}/{id?}");
        });
    }
}
Lukas
  • 1,699
  • 1
  • 16
  • 49
CJ Scholten
  • 623
  • 2
  • 13
  • 27

7 Answers7

26

For me, Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler was logging this error: '".AspNetCore.Correlation.OpenIdConnect.84ee_7zFbvb_w264b0SPRmS1OTKCeDhmzQ6awHoJ5gA"' cookie not found.

After looking at Chrome developer tools, I could see that the Correlation cookie was being stripped by the browser because the cookie's SameSite attribute was set to "None" yet the "Secure" attribute was not set. Chrome didn't like this.

I added the following statement in my Startup.Configure method.
*NOTE: This has to be added before app.UseAuthentication() and app.UseAuthorization().

app.UseCookiePolicy(new CookiePolicyOptions
{
    Secure = CookieSecurePolicy.Always
});
Scope Creep
  • 811
  • 7
  • 14
  • same problem and the same solution, thanks! – anatol Dec 15 '21 at 04:50
  • I'm having exactly the same problem using Dudende. Did you add it to the identity server startup class or the client application? – Ali Nov 09 '22 at 08:55
8

I have resolved this issue by adding below code

 options.Events = new OpenIdConnectEvents
                {
                    OnMessageReceived = OnMessageReceived,
                     OnRemoteFailure = context => {
                         context.Response.Redirect("/");
                         context.HandleResponse();

                         return Task.FromResult(0);
                     }

                };
MayankGaur
  • 957
  • 11
  • 22
  • 1
    This solution is little bit hacky – georgesolc Jan 31 '20 at 14:47
  • 1
    This is exactly what I am looking for. I don't want the user to know about the innate errors regarding the login process (not conducive to a positive user experience). I just want him to be thrown to the main page (for now - I'll add a redirect to an error page later). – lohithbb Oct 20 '20 at 10:25
  • this worked. Thanks! – RDeveloper Jan 14 '22 at 16:20
  • worked for me as well, thank you. – minimalist_zero Mar 11 '22 at 16:09
  • But what happens when you're providing an identity server solution and you don't have access to the clients' application source code to add this configuration? :/ I'm using Dudende and I have a .net core web app as a client, the code you posted fixes the issue but the business can't change any clients' app configuraiton – Ali Nov 09 '22 at 10:23
4

In order to get it working, I had to combine Jeff Tian's solution with Scope Creep's solution:

app.UseCookiePolicy(new CookiePolicyOptions
{
    MinimumSameSitePolicy = SameSiteMode.None,
    Secure = CookieSecurePolicy.Always
});

Then I also had to add an extra line to ConfigureServices:

options.Cookie.SameSite = SameSiteMode.None;

services.AddAuthentication(options =>
    {
        options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
        options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
    })
    .AddCookie(options =>
    {
        options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
        options.Cookie.SameSite = SameSiteMode.None;  // I ADDED THIS LINE!!!
        options.LoginPath = "/home/NotAuthorized";
...

Turning on Sessions in Asp.net also fixes the issue, and MayankGaur's solution also works, but I really didn't want to suppress errors that might make things easier to debug in the future.

Mike Olund
  • 419
  • 5
  • 6
2

The problem was that the IdentityServer was still using AddDeveloperSigningCredential

which worked fine locally, but not in Azure. By adding a certicate and this code it worked perfectly:

X509Certificate2 cert = null;
using (X509Store certStore = new X509Store(StoreName.My, StoreLocation.CurrentUser))
{
    certStore.Open(OpenFlags.ReadOnly);
    X509Certificate2Collection certCollection = certStore.Certificates.Find(
        X509FindType.FindByThumbprint,
        // Replace below with your cert's thumbprint
        "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX",
        false);
    // Get the first cert with the thumbprint
    if (certCollection.Count > 0)
    {
        cert = certCollection[0];
        Log.Logger.Information($"Successfully loaded cert from registry: {cert.Thumbprint}");
    }
}

// Fallback to local file for development
if (cert == null)
{
    cert = new X509Certificate2(Path.Combine(_env.ContentRootPath, "example.pfx"), "exportpassword");
    Log.Logger.Information($"Falling back to cert from file. Successfully loaded: {cert.Thumbprint}");
}
CJ Scholten
  • 623
  • 2
  • 13
  • 27
2

Finally I fixed it by

  app.UseCookiePolicy(new CookiePolicyOptions
        {
            MinimumSameSitePolicy = SameSiteMode.None
        });

Before applying the change, I noticed the cookie's same site was marked as Strict.

I changed the code to SameSiteMode.Lax, and it didn't work.

So I changed again to the SameSiteMode.None, it works!

Jeff Tian
  • 5,210
  • 3
  • 51
  • 71
0

Make sure the user didn't bookmark your login page instead of your home page. This was the problem I had with Cognito.

Andrew
  • 761
  • 7
  • 9
0

Like Mike, it worked for me to set a cookie policy on the app and the authentication. Later I realized that the reason it worked was that I went from Strict to Lax. So I could now remove the option from the authentication builder, and it would still work.

I ended up changing my code to:

app.UseCookiePolicy(new CookiePolicyOptions
{
    Secure = CookieSecurePolicy.Always,
    MinimumSameSitePolicy = SameSiteMode.Lax
});

app.UseIdentityServer();
app.UseAuthorization();
...

By the way, you might want to stay away from using None as your cookie can be rejected.

None—This can only be used if the cookie is also marked Secure; setting SameSite=None without the Secure flag may lead to the cookie being rejected.

https://andrewlock.net/understanding-samesite-cookies/