AspNet Core Identity - cookie not getting set in production - authentication

I have a .NET Core 2 web app and I want to use ASP.NET Identity to authenticate my users. On .NET Core 1.x, my code was working fine.
I migrated to .NET Core 2, and authentication works when running locally in Visual Studio. But when I deploy to a live environment, authentication stops working: the authentication cookie isn't being set in production.
My Startup.cs code looks like this:
public void ConfigureServices(IServiceCollection services)
{
services.AddIdentity<AppUser, RavenDB.IdentityRole>()
.AddDefaultTokenProviders();
...
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
...
app.UseAuthentication();
}
To sign in, my code looks like this:
public async Task<ActionResult> SignIn(...)
{
var user = ...; // Load the User from the database.
await this.signInManager.SignInAsync(user, isPersistent: true);
...
}
This code works locally: the ASP.NET Identity auth cookie is set. However, when I deploy this to production enviro in Azure, the cookie never gets set.
What am I missing?

I solved the problem. It boiled down to HTTPS: it appears that signInManager.SignInAsync(...) sets a cookie that is HTTPS-only. I was publishing to a non-HTTPS site initially for testing.
Once I published to an HTTPS site, the cookie started working again.
The reason it was working locally was that I was running in HTTPS locally.

Had same problem with Chrome 60+. Cookie did not want to set on HTTP site or even HTTPS and Cordova.
options.Cookie.SameSite = SameSiteMode.None;
https://github.com/aspnet/Docs/blob/master/aspnetcore/security/authentication/cookie.md
Changing from default value (Lax) to None fixed it for me.

I had similar issue. In the startup.cs file, I had to change
app.UseCookiePolicy(new CookiePolicyOptions
{
Secure = CookieSecurePolicy.Always
});
to
app.UseCookiePolicy(new CookiePolicyOptions
{
Secure = CookieSecurePolicy.SameAsRequest
});
This allowed cookie authentication to work on both http and https.

Related

Asp.Net Core configure Identity authentication middleware properly

Requirement is that I have MVC & WebAPI in the project. MVC views will be delivered for initial
rendering like Login, base views of features (ex:- Users/Index, Dashboard/Index etc..) Web APIs will be used for other work within these views via AJAX with JWT.
I am using Asp.Net core Identity for user management related work running on .Net 5.0
I am confused with configuring multiple identity schemes and the proper configuration of authentication/authorization pipeline in conigureservices() & configure() in startup.
To configure multiple auth schemes I referred to https://stackoverflow.com/a/64887479/2058413 since it's done using same versions that I use. So my startup methods are below which is similar to the code in that thread.
public void ConfigureServices(IServiceCollection services)
{
string connectionString = Configuration.GetConnectionString("default");
services.AddDbContext<AppDBContext>(c => c.UseSqlServer(connectionString));
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<AppDBContext>();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(x =>
{
x.LoginPath = "/Account/Login";
x.ExpireTimeSpan = TimeSpan.FromMinutes(10d);
x.AccessDeniedPath = "/Account/Register";
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("123456")),
ValidateIssuer = false,
ValidateAudience = false
};
});
services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(CookieAuthenticationDefaults.AuthenticationScheme, JwtBearerDefaults.AuthenticationScheme);
defaultAuthorizationPolicyBuilder = defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
services.AddControllersWithViews();
}
My App configure method is below
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Then the test method in controller(where user should get redirected to after authentication) is below
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
public IActionResult Index()
{
return View();
}
To my understanding the order of the pipeline configuration is correct. Now here are the problems I face.
As specified in .AddCookie(option=>) , user doesn't get redirected to login page. However, if I remove the JwtBearerDefaults.AuthenticationScheme from the services.AddAuthorization(…) it gets redirected to login page properly. Why is that?
So I remove JwtBearerDefaults.AuthenticationScheme; which takes me to login and after successful login I can see that HttpContext.User.Identity.IsAuthenticated is set to true. But it doesn't redirect to Home/Index. No errors thrown and in browser console [network tab] it shows a 302 and redirect back to login. Since I have added [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)] to Index method in HomeController, I tried removing the scheme and adding [Authorize] and tried again. Still it gets redirected to login page. This is another thing I am confused about.
So I removed everything related to dual scheme authentication and left the ConfigureService() with below code
string connectionString = Configuration.GetConnectionString("default");
services.AddDbContext<AppDBContext>(c => c.UseSqlServer(connectionString));
services.AddIdentity<IdentityUser, IdentityRole>()
.AddEntityFrameworkStores<AppDBContext>();
services.AddControllersWithViews();
Now everything works fine (redirection to login if not authenticated and also redirects to /Home/Index after authorization).
I went through below links as well about multi scheme authentication, but I am still confused with this pipeline configuration.
ASP.NET Core WebAPI Cookie + JWT Authentication
https://wildermuth.com/2017/08/19/Two-AuthorizationSchemes-in-ASP-NET-Core-2
https://mitchelsellers.com/blog/article/using-multiple-authentication-authorization-providers-in-asp-net-core
I need help only to this multi-scheme authentication pipeline configuration part.
Ok, after some research the main issue was;
I have mixed up old ways of registering services in StartUp (asp.net core 3.x). So had to clear up all those. Resources that helped me to do that;
Migrate from ASP.NET Core 3.1 to 5.0
ASP.NET Core Middleware
This cleaned up a lot of unnecessary code since in .Net 5 there were some shortcuts that could be used.
Order of service registrations. This may depend on what kind of services you are using, but in my case the order was something like below:
AddIdentity
setup Cookie & JWT authentication c)
My Multitenant stuff
AddCors
AddModules (will be option for some of you. I use it to load plugins dynamically)
Other stuff (However, even in these places the order might matter depending on
what you do)
The other thing was, I had to remove ConfigureApplicationCookie() since AddIdentity seems to be doing that. Also in AddAuthorization() earlier I had code to specify what are the default schemes i wanted to use (Cookie & JWT). But now I had to remove all that. Reason is Identity takes over the cookie scheme and when I specify below, JWTAuth takes over.
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
Generally setting up Startup properly seems to be tricky depending on different services you use. Order will matter

Asp.Net Core & IIS Server : session cookie not set in production

I am working on an ASP.NET Core web application. My dotnet version is 3.1.401 and I am with Visual Studio Community 2019 Version 16.7.2. I am using a session cookie that was working well with IIS Express.
I deployed the app on IIS Server v 10.0.19041.507. My authentication cookie (without ASP.NET Core Identity) is working fine but on the contrary the session cookie is not set.
Here is what I have in startup.cs
services.AddSession(options =>
{
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.HttpOnly = true;
options.Cookie.Name = ".FjDRegisterLLAT.Session";
//options.IdleTimeout = TimeSpan.FromSeconds(900);
options.Cookie.IsEssential = true;
});
Let me precise that I removed the line : "app.UseCookiePolicy()" from startup.cs
In IIS, there is an option for the session state, but I really don't know how to configure this. Perhaps I should not even configure it as my authentication cookie is working and I didn't not change anything to it.
I still tried using the name of my cookie (see in statup.cs above) in place of the default one, but it does not change anything :
Here is a controller where I'am using the session cookie to retrieve data. I thought that when I would make a request to that controller, a value would be set along with the creation of the cookie like in development mode, but no !
[HttpPost]
public IActionResult Index(bool isCheckingOut)
{
User user = GetUser();
user.isCheckinOut = isCheckingOut;
HttpContext.Session.SetComplexData("user", user);
return RedirectToAction("SetStatus");
}
UPDATED_1
In development mode, my cookie is set with the name and options as set in Startup.cs.
UPDATED_2
Apparently it has nothing to do with the application settings nor the IIS Server configuration. The session cookie is set properly by Edge and FireFox. It turns out that chrome does not want it.

ASP.NET Core 2.2 Authentication not working after deploying the app - Nginx with mutliple apps

I'm trying deploy my application to our internal server with Nginx. It's an ASP.NET Core 2.2 Razor Pages site. I was asked to include some authentication for logging purposes. Eventually everything was working fine on my computer. I used this site to add cookie based authentication:
https://www.mikesdotnetting.com/article/335/simple-authentication-in-razor-pages-without-a-database
I did some modifications to handle a few more users within the OnPost() method. Though I don't think that would be the problem.
It might be important to mention this is not the only .net core app running on the server. The setup is similar to this:
app1: our.domain.com
app2: our.domain.com/app2 (this is the one I have problems with)
everything works properly except the login. When I try to log in, if the password and username is correct it gets redirected to the proper page, however it seems like there is no identity or it cannot find it afterwards.
On my first attempt, I found the following error in the kestrel service log:
fail: Microsoft.AspNetCore.Antiforgery.Internal.DefaultAntiforgery[7]
I could make that disappear with using services.AddDataProtection(), however the problem is still the same, I get redirected - or get the error message if the login attempt is incorrect - but still can't access the authorised folder and e.g. the HttpContext.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.GivenName)?.Value returns null or empty.
I did a quick test and added the same login page and an authorised folder, and the other dependencies to the other app. And there it works. I didn't even included services.AddDataProtection() in the startup.cs. The login works perfectly. Though it's using .net core 2.1.
So it might have to do something with the rooting? Or I don't know. I'm totally lost. I'm not a full time developer, more like a hobbyist and I'm completely stuck at this moment. Maybe I messed up something within the startup.cs? Or I should add something else? Or is it something with the cookies handling? I did make a lot of searching, no luck so far.
Here is the relevant part of my startup.cs:
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.AddEntityFrameworkNpgsql()
.AddDbContext<Models.UserAccessDbContext>(options =>
options.UseNpgsql(Configuration.GetConnectionString("appConnection")))
.BuildServiceProvider();
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
// https://hanselman.com/blog/DealingWithApplicationBaseURLsAndRazorLinkGenerationWhileHostingASPNETWebAppsBehindReverseProxies.aspx
services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.All;
options.AllowedHosts = Configuration.GetValue<string>("AllowedHosts")?.Split(';').ToList<string>();
});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(cookieOptions =>
{
cookieOptions.LoginPath = "/";
});
services.AddMvc().AddRazorPagesOptions(options =>
{
options.Conventions.AuthorizeFolder("/admin");
}).SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services.AddDataProtection()
.SetApplicationName("app")
.PersistKeysToFileSystem(new DirectoryInfo(#"/var/dpkeys/"));
}
// 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.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
app.Use((context, next) =>
{
context.Request.PathBase = new PathString("/app");
return next.Invoke();
});
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc();
}
Update:
One small detail. The cookie is created and I can see it within the Chrome's inspector. But the site/app doesn't see me as an authenticated user.
Seems I had problems with the cookie settings. One more difference between the two pages, that one if it uses font-awesome, which means I have some external cookies on my site. As a wild guess, first I set
options.MinimumSameSitePolicy = SameSiteMode.Lax;
since the other cookies had that mode.
And just like that it started to work on the hosting server as well! Now the app recognises the cookie created after the log in.

Add Session storage to ASP.NET Core 3.0

We are currently migrating an existing ASP.NET Core 2.2 web application to 3.0 So far we've got most things working, except session storage.
We had this fully working in v2.2 as we used it to hold the current logged in user's details. Now that we've upgraded to v3.0 it no longer works.
Here's the middleware code.
public void ConfigureServices(IServiceCollection services)
{
// configure Razor pages, MVC, authentication here
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
//prevent session storage from being accessed from client script
//i.e. only server side code (added security)
options.Cookie.HttpOnly = true;
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseSession();
}
N.B. I've removed the rest of the middleware code for clarity.
I've tried moving the app.SetSession() line to the top of the method in case the order of execution was the problem but this has made no difference.
When I hover over the HttpContent.Session property in the debugger I get the following error:
HttpContext.Session threw an exception of type System.InvalidOperationException
How do I enable Session storage in ASP.NET Core 3.0?
I've just tried adding the app.UseSession() to the top of the method and it's working now. It definitely didn't work before but it's working now and that's the main thing.

Aspnet Core with Adfs 2016 OpenId can't sign out

I setup an MVC project with Aspnet Core targeting Net461. Authentication is configured to use Adfs from a Windows Server 2016 system. I managed to get sign in working, however, when I click sign out I am given a page cannot be displayed error. Browsing back to the home url shows that the user is still logged in also. Any suggestions?
You might find this sample useful (even though it is for Azure ADFS, it works for local installs as well): https://github.com/Azure-Samples/active-directory-dotnet-webapp-openidconnect-aspnetcore
The logout action method like the following work well in my case:
[HttpGet]
public IActionResult SignOut()
{
var callbackUrl = Url.Action(nameof(SignedOut), "Account", values: null, protocol: Request.Scheme);
return SignOut(
new AuthenticationProperties { RedirectUri = callbackUrl },
CookieAuthenticationDefaults.AuthenticationScheme,
OpenIdConnectDefaults.AuthenticationScheme);
}
This will redirect you to the /Account/SignedOut after it completes and you need to register your /signout-callback-oidc endpoint for your client as well. This endpoint is used (by default) by the OIDC ASP.NET Core middleware.