I recently ran into a problem with my net5.0 application (upgraded from net core 3.1). I am using Microsoft.AspNetCore.Identity to sign in and it works perfectly fine. After deploying it to the production machine the login is still working and I receive my cookie. But after calling the url on the next day I am not logged in despite having my auth cookie (it's valid for 30 days). On my local machine hosted on IIS it works fine. Here are parts of my code:
Startup.cs:
ConfigureServices
services.ConfigureApplicationCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(30);
options.SlidingExpiration = true;
options.LoginPath = new PathString("/Account/Login");
options.LogoutPath = new PathString("/Account/Logoff");
options.Cookie.Name = "MyApplication";
options.Cookie.IsEssential = true;
});
I am using the ProtectPersonalData attribute to encrypt userdata in the database with a keyring:
services.AddIdentity<ApplicationUser, IdentityRole>(config =>
{
config.SignIn.RequireConfirmedEmail = true;
config.Stores.ProtectPersonalData = true;
config.Stores.MaxLengthForKeys = 128;
})
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
Configure
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
And this is the cookie in Firefox:
[cookie][1]
In my understanding this cookie should be send to the server on every request for that domain. And even though Firefox states, that it was accessed last on the exact time of my request it has no effect.
If you need any further code let me know. Any help is appreciated!
[1]: https://i.stack.imgur.com/UTSuR.png
Seems like everything was ok with my code. The error was within the IIS Configuration. As I am using DataProtection the ApplicationPool should have "load userprofile" set to true, because otherwise it can not store the keyring and data is lost after application shutdown/restart. After changing that on the production server it seems to be working now.
Related
I've created a Blazor Server project using the standard Visual Studio 2022 template with authentication set to Microsoft Identity. It works locally without issue.
When I try to deploy it to the default website on an IIS server in a virtual application, it gives the following error:
Program.cs:
var builder = WebApplication.CreateBuilder(args);
var initialScopes = builder.Configuration["DownstreamApi:Scopes"]?.Split(' ') ??
builder.Configuration["MicrosoftGraph:Scopes"]?.Split(' ');
builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
.AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
.EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
.AddMicrosoftGraph(builder.Configuration.GetSection("MicrosoftGraph"))
.AddInMemoryTokenCaches();
builder.Services.AddControllersWithViews()
.AddMicrosoftIdentityUI();
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy
options.FallbackPolicy = options.DefaultPolicy;
});
builder.Services.AddRazorPages();
builder.Services.AddServerSideBlazor()
.AddMicrosoftIdentityConsentHandler();
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.MapBlazorHub();
app.MapFallbackToPage("/_Host");
app.Run();
I think it is an issue with the return url, because the virtual application name is added to the address automatically. I have this url included in my app registration, but it still doesn't work.
I think after you see the error message page above, you may try to visit http://localhost or maybe you change the default home page url so http://localhost/the_word_you_covered and see if the user already signed in successfully.
I created a new Blazor server application with Microsoft Identity, then I fill the appsettings.json with tenant id, client id, and leave the CallbackPath as /signin-oidc by default. And in AAD, set redirect URL as https://localhost/signin-oidc. It worked well when test locally. After published to IIS, the sign in didn't work well so I comment the
builder.Services.AddAuthorization(options =>
{
// By default, all incoming requests will be authorized according to the default policy
options.FallbackPolicy = options.DefaultPolicy;
});
and content in the so that I can see more error issue.
if (!app.Environment.IsDevelopment()) { }
But after doing these steps, everything worked well except the sign in redirect back to http://localhost/signin-oidc. Pls note here we need to set the redirect URL as http in Azure AD portal because IIS uses HTTP by default. But since there's no page routing to signin-oidc.
=========================================
If what I said above is not the issue, I'm afraid you can create a self-signed certificate and bind it to the default website, then visiting https instead. Just now I tried again and it keep showing 500 error like screenshot below. Then I create self-signed certificate to use https then solved the 500 error.
I recently upgraded my ASP.Net Core MVC web app to .Net Core 3.1, and ever since, the Google External login cookie hasn't persisted properly as it did before the upgrade. The code itself didn't change at all, the app was just upgraded from .Net Core 2.0 to .Net Core 3.1.
The login flow works (user clicks login button, Google login screen appears, user logs in with Google Credentials/selects existing Google user and is sent back to the web app), but at random times (sometimes 30-60 seconds after login), the user's session randomly ends and the user is redirected back to the login screen. I've tested this extensively on localhost and the issue does not occur, but happens frequently on the hosting service I'm using (MochaHost).
Has anyone else had this issue with .Net Core 3.1?
The ConfigureServices method used at startup is below for reference.
public void ConfigureServices(IServiceCollection services)
{
services.AddLogging();
services.AddResponseCompression(options =>
{
options.Providers.Add<GzipCompressionProvider>();
options.EnableForHttps = true;
});
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseMySql(Configuration["ConnectionStrings:MySQL"]));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddUserStore<UserStore>()
.AddDefaultTokenProviders();
services.AddAuthentication().AddGoogle(options =>
{
options.ClientId = Configuration["Authentication:Google:ClientId"];
options.ClientSecret = Configuration["Authentication:Google:ClientSecret"];
options.CallbackPath = new PathString("/signin-google");
});
services.ConfigureApplicationCookie(options =>
{
options.AccessDeniedPath = "/error/401";
options.Cookie.Name = "MyApp";
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(180);
options.LoginPath = "/login";
options.ReturnUrlParameter = CookieAuthenticationDefaults.ReturnUrlParameter;
options.SlidingExpiration = true;
});
services.AddMvc(option => option.EnableEndpointRouting = false);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddSingleton<IFileProvider>(new PhysicalFileProvider(Configuration.GetValue<string>("RootFilePath")));
var serviceAccountFilePath = GetGoogleServiceAccountCredentialPath();
var googleCredential = GoogleCredential.FromFile(serviceAccountFilePath);
services.AddSingleton(StorageClient.Create(googleCredential));
}
In my case, the issue was that Data Protection key storage was not available via user profile or HKLM registry after the .Net Core upgrade. I'm not sure if this is related to having to switch to 64-bit app pool mode after the upgrade to .Net Core 3.1 or not, but the bottom line is data protection keys were only being stored in memory. After shifting from server to server via the load balancer, the user keys were lost, causing a redirect back to the login page.
Sample Log entries:
2020-11-20 09:00:21.162 -08:00 [INF] Starting web host
2020-11-20 09:00:21.572 -08:00 [WRN] Using an in-memory repository. Keys will not be persisted to storage.
2020-11-20 09:00:21.574 -08:00 [WRN] Neither user profile nor HKLM registry available. Using an ephemeral key repository. Protected data will be unavailable when application exits.
I had to add the below code to use a folder on the hosting service to store key files so the user's session would persist:
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo(Configuration.GetValue<string>("KeyStorePath")));
I have made asp net core web app with Angular. It is using authentication based on cookies using standard net core authentication mechanism. All is working fine until IIS restart (recycle). After application restarted all users becomes unauthenticated and needs to relogin.
May be some one knows what should be done to make information stored in cookies be actual for several days and not depending on application restart.
This is the piece of code
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyAppContext>(options => options.UseMySql(connectionString));
services.AddIdentity<ApplicationUser, ApplicationRole>(options =>
{
options.User.RequireUniqueEmail = true;
options.SignIn.RequireConfirmedEmail = true;
})
.AddEntityFrameworkStores<MyAppContext>()
.AddDefaultTokenProviders();
services.AddAuthorization();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.SlidingExpiration = true;
options.ExpireTimeSpan = System.TimeSpan.FromDays(7);
options.LoginPath = $"/Identity/Login";
options.LogoutPath = $"/Identity/Logout";
options.Cookie.IsEssential = true;
});
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
}
Asp.Net Core uses the Data Protection mechanism to generate temporary encryption keys. and with the restart of the server or IIS, these keys are lost and re-generated.
In order that the keys for encrypting web application information are stored permanently and not lost with the server restart you can go to Application pool setting in IIS and set Load user profile to True
In this case, the keys will be permanently stored in the user's profile folder for the application's application pool, encrypted by the Windows DPAPI mechanism.
Or you can check these links 1,2 to keep the login status after iis reset
I have a .net core project using Microsoft.AspNetCore.Authentication (2.2.0) configured to use CookieAuthentication. Cookies are configured to be persistent and expire after seven days. The problem is that all logged-in users are logged out whenever the application pool is recycled.
I am not using sessions at all. I have verified that the cookie is still present in the web browser, it seems like the existing cookies are determined to be invalid by the server. How can I change this behavior?
This is the current configuration:
services
.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(
CookieAuthenticationDefaults.AuthenticationScheme,
options =>
{
options.AccessDeniedPath = "/";
options.LoginPath = "/";
options.LogoutPath = "/Authentication/Logout";
options.Events.OnRedirectToLogin = context =>
{
context.Response.Redirect("/?returnUrl=" + context.Request.GetEncodedPathAndQuery());
return Task.CompletedTask;
});
The error was an error in the configuration of the application pool in IIS. In the the "Advanced settings" of the application pool the setting "Load User Profile" needs to be set to "true".
I'm creating a new WebApp using ASP.NET Core 2.2 and Razor Pages and I want to authenticate users using AzureAD. This works fine locally with localhost and I can sign in and out with no problem. But when I publish it to azure I can not sign in. After the microsoft sign in pages I am redirected to my WebApp to the page "/.auth/login/done" that says: "You have successfully signed in. Return to the website" and I can return to my website but I am not logged in.
In the App Registration in Azure I have configured the redirect Urls for localhost and for the application. For localhost it looks like "https://localhost:44321/.auth/login/aad/callback" and for the app something like "https://maywebapp.azurewebsites.net/.auth/login/aad/callback".
I configured the App to use always https to make sure that the Url is the same as the configured in Azure.
This is my Service Configuration:
services.AddAuthentication(options =>
{
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddOpenIdConnect(options =>
{
options.Authority = Configuration["Microsoft:Authority"];
options.ClientId = Configuration["Microsoft:ClientId"];
options.CallbackPath = Configuration["Microsoft:CallbackPath"];
options.ResponseType = OpenIdConnectResponseType.IdToken;
options.SignedOutRedirectUri = Configuration["Microsoft:SignedOutRedirectUri"];
options.TokenValidationParameters.NameClaimType = "name";
})
.AddCookie();
I expect the same behaviour as when I am running the working App locally.
To make all accesses to pages in this folder be authenticated, we could add the Authorize attribute to all page model classes, but we can do better than that. Going back to the Startup class, we can add some Razor Pages conventions to make our lives easier.
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc()
.AddRazorPagesOptions(options =>
{
options.Conventions.AuthorizeFolder("/Account");
});
...
}
Here is an article about authenticate with asp.net razor.