Aspnet Core Identity Redirects to HTTP even UseHttpsRedirection defined in startup - asp.net-core

Having scaffolded the Identity Area in Aspnet Core 2.1. with this startup class:
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)
{
// AppSettings.json
//
services.Configure<AppSettings>(Configuration.GetSection("ProvidersSettings"));
// IdentityUser settings
//
services.Configure<IdentityOptions>(options =>
{
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings.
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._#+";
options.User.RequireUniqueEmail = true;
});
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<IdentityUser>()
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddMvc(options =>
{
// All MvcControllers are hereby "Authorize". Use AllowAnonymous to give anon access
//
var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
options.Filters.Add(new AuthorizeFilter(policy));
options.Filters.Add(new AutoValidateAntiforgeryTokenAttribute());
});
// IdentityModel
//
services.AddSingleton<IDiscoveryCache>(r =>
{
var url = Configuration["ProvidersSettings:IdentityServerEndpoint"];
var factory = r.GetRequiredService<IHttpClientFactory>();
return new DiscoveryCache(url, () => factory.CreateClient());
});
// HttpContext injection
//
services.AddHttpContextAccessor();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, UserManager<IdentityUser> userManager, ApplicationDbContext dbContext)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
}
It works in development. But in production it doesn't stay on https. It redirects to http and this path:
/Identity/Account/Login?ReturnUrl=%2F
On localhost it works correctly with both IIS express and "program".
Any ideas are most helpful. Thanks.

Related

ASP .NET Core CORS issue with Google authentication on redirect

Been following this tutorial in order to implement Google authentication in my web API but on the client side (using React and axios to do the request) the authentication process gets interrupted with this CORS issue and I'm struggling to sort it out:
Access to XMLHttpRequest at 'https://accounts.google.com/o/oauth2/v2/auth?(etc)' (redirected from 'https://localhost:44320/Photo/b997d788-3812-41d0-a09d-1a597eee9bad') from origin 'https://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
This is the Startup.cs file:
namespace rvc
{
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.AddCors(options =>
{
options.AddDefaultPolicy(builder =>
{
builder.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod();
});
});
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(options =>
{
options.LoginPath = "/account/google-login";
}).AddGoogle(options =>
{
options.ClientId = "clientId";
options.ClientSecret = "secret";
});
services.AddScoped<PhotoService>();
services.AddScoped<TagService>();
services.AddScoped(_ => new BlobServiceClient(Configuration.GetConnectionString("AzureBlobStorage")));
services.AddDbContext<Data.DataContext>(x => x.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddControllers().AddJsonOptions(options =>
{
options.JsonSerializerOptions.ReferenceHandler = ReferenceHandler.IgnoreCycles;
});
services.AddSwaggerGen(c => { c.SwaggerDoc("v1", new OpenApiInfo { Title = "rvc", Version = "v1" }); });
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "rvc v1"));
}
app.UseHttpsRedirection();
if (env.IsProduction())
{
app.UseSpa(spa => { });
app.UseFileServer(new FileServerOptions
{
FileProvider = new PhysicalFileProvider(
Path.Combine(env.ContentRootPath, "client")),
EnableDefaultFiles = true
});
}
app.UseRouting();
app.UseCors();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => { endpoints.MapControllers(); });
}
}
}
The Route("google-login") gets called but the Url.Action("GoogleResponse") is not reached. These are the Google Authentication methods:
namespace rvc.Controllers;
[AllowAnonymous, Route("account")]
public class AccountController : Controller
{
[Route("google-login")]
public IActionResult GoogleLogin()
{
var properties = new AuthenticationProperties {RedirectUri = Url.Action("GoogleResponse")};
return Challenge(properties, GoogleDefaults.AuthenticationScheme);
}
[Route("google-response")]
public async Task<IActionResult> GoogleResponse()
{
var result = await HttpContext.AuthenticateAsync(CookieAuthenticationDefaults.AuthenticationScheme);
var claims = result.Principal?.Identities.FirstOrDefault()
?.Claims.Select(claim => new
{
claim.Issuer,
claim.OriginalIssuer,
claim.Type,
claim.Value
});
return Json(claims);
}
}
This is probably because from the server you use redirect, which triggers CORS (even if from your server you allow it).
you have to return the redirect URL to your front-end in some other way, capture it from the front-end app and then call the URL you need to invoke.

PasswordSignIn return Succeed but after redirect to another ActionResult User is not authenticated in net core 3.1

I user net core 3.1 and EF core to identity and login.
At first , I use passwordSignIn method to signin and returns Succeed after that I retdirectToAction to "profile".
in "Profile" User.Identity.isAuthenticated is false.
As you can see in my code I set sign in complete and works correct. but user is not authenticeted.
here is my sign in:
[HttpPost]
public async Task<IActionResult> SignUp(string username, string password)
{
var user = _db.Users.Where(p => p.UserName == username).FirstOrDefault();
if (user != null)
{
var res = await _signInManager.PasswordSignInAsync(user, password, true, false);
if (res.Succeeded)
{
return RedirectToAction("profile");
}
}
return View();
}
and here is profile:
public IActionResult Profile()
{
var t = User.Identity.IsAuthenticated;
var n = User.Identity.Name;
var s = User.Claims.ToList();
var x = _userManager.GetUserId(User);
var ss = User.IsInRole("Admin");
return View();
}
and here is my start up :
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.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;
});
services.AddControllersWithViews();
services.AddDbContext<MyContext>(opt =>
{
opt.UseSqlServer(Encryptor.Decrypt(Configuration.GetConnectionString("DefaultConnection")));
});
var builder = services.AddIdentityCore<User>();
var identityBuilder = new IdentityBuilder(builder.UserType, builder.Services);
identityBuilder.AddRoles<UserRole>();
identityBuilder.AddEntityFrameworkStores<MyContext>();
identityBuilder.AddSignInManager<SignInManager<User>>();
services.ConfigureApplicationCookie(options =>
{
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(30);
options.LoginPath = "/Account/Login";
options.AccessDeniedPath = "/Account/AccessDenied";
options.SlidingExpiration = true;
});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie("Identity.Application");
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
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: "Admin",
pattern: "{area:exists}/{controller=Admin}/{action=Index}/{id?}");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Update 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.AddControllersWithViews();
services.AddDbContext<MyContext>(opt =>
{
opt.UseSqlServer(Encryptor.Decrypt(Configuration.GetConnectionString("DefaultConnection")));
});
var builder = services.AddIdentityCore<AppUser>();
var identityBuilder = new IdentityBuilder(builder.UserType, builder.Services);
identityBuilder.AddRoles<Role>();
identityBuilder.AddEntityFrameworkStores<MyContext>().AddDefaultTokenProviders();
identityBuilder.AddSignInManager<SignInManager<AppUser>>();
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.ConsentCookie.IsEssential = true;
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.Configure<IdentityOptions>(options =>
{
options.SignIn.RequireConfirmedEmail = false;
options.SignIn.RequireConfirmedAccount = false;
options.SignIn.RequireConfirmedPhoneNumber = false;
});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie("Identity.Application");
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "Admin",
pattern: "{area:exists}/{controller=Admin}/{action=Index}/{id?}");
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
Finally I solve problem with the help of #Yinqiu and a bit more search.
I add these lines to signin method:
var claims = new[]
{
new Claim("name", authUser.Username)
};
var identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, new ClaimsPrincipal(identity));
I dont have any idea about how it solve but it works.
of course I change line in start up class :
... .AddCookie("Cookie");

Asp.net core 3.1 backend does not get the Identity from cookie

I have vue.js frontend and asp.net core 3.1 backend. Backend uses SignInManager and Identity. I am trying to use cookie authentication. Api requests work from Postman(!!) roles are applied, everything, but do not from vue app (httpContext.User.Identity.IsAuthenticated is false). Indentity is empty. Cookie is present in the HttpContext
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyDBContext>(options => options.UseSqlServer(Configuration["ConnectionStrings:Default"]));
services.AddCors();
services.AddControllers()
services.AddIdentity<AppUser, IdentityRole>(
opts =>
{
opts.SignIn.RequireConfirmedEmail = true;
}
)
.AddSignInManager<SignInManager<AppUser>>()
.AddEntityFrameworkStores<MyDBContext>()
.AddDefaultTokenProviders();
services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
options.Cookie.SameSite = SameSiteMode.None;
options.SlidingExpiration = true;
});
//some DI
...
//
}
bit more Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, UserManager<AppUser> userManager)
{
app.UseRouting();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseCors(x => x
.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
I was trying to proxy requests to api in vue app. Did not help
module.exports = {
devServer: {
proxy: {
'^/api': {
target: 'https://localhost:44376',
ws: true,
changeOrigin: true
}
}
}
}
What can be wrong?
Maybe you are missing AddRoles<IdentityRoles>()?
services.AddIdentity<AppUser, IdentityRole>(
opts =>
{
opts.SignIn.RequireConfirmedEmail = true;
}
)
.AddRoles<IdentityRoles>() .// <== this line
.AddSignInManager<SignInManager<AppUser>>()
.AddEntityFrameworkStores<MyDBContext>()
.AddDefaultTokenProviders();

ASP Core 3 react template, HttpContext.User.IsAuthenticated() returns False after login

After working on my project for a while, I released the HttpContext.User.IsAuthenticated() returns False after login and I need to know where I should look for the mistake I made that cause this problem.
This is the Login, OnPost method.
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
var user = _userManager.Users.FirstOrDefault(u => u.StudentNumber == Input.StudentNumber.ToString());
if (!(user is null) && await _userManager.CheckPasswordAsync(user, Input.Password))
await _signInManager.SignInAsync(user, Input.RememberMe);
var isUserAuthenticated = HttpContext.User.IsAuthenticated();
return Redirect(returnUrl);
}
// If we got this far, something failed, redisplay form
return Page();
}
The ConfigureServices method.
public void ConfigureServices(IServiceCollection services)
{
services.AddAutoMapper();
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDefaultIdentity<ApplicationUser>(option=>option.Password.RequireNonAlphanumeric=false)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddMvc(options => options.EnableEndpointRouting = false)
.AddNewtonsoftJson();
// In production, the React files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/build";
});
}
The Configure method.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
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.UseHttpsRedirection();
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseAuthentication();
app.UseIdentityServer();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseReactDevelopmentServer(npmScript: "start");
}
});
}
SignInManager.SignInAsync() only creates the cookie for the given user. This method would not set HttpContext.User.
But in the next request which has the cookie you can access HttpContext.User after AuthenticationMiddleware and HttpContext.User.IsAuthenticated() should be true.
AuthenticationMiddleware always try to authenticate user with the default scheme and since you have AddIdentityServer after AddDefaultIdentity, identity server is becoming your default scheme, but when you call SignInManager.SignInAsync the Identity scheme is triggered.
To sum up, with this configuration your AuthenticationMiddleware always tries to authenticate request for IdentityServer and if you want other scheme for you apis you should use [Authorize(AuthenticationSchemes = "Identity.Application")].
P.S. Identity.Application is authenticatio scheme for ASP.NET Identity

How to access Session in OpenIdConnect TokenValidated even handler

I have an ASP.NET Core 2.1 MVC application in which I have configured OpenIdConnect provider for authentication. The Startup class looks like below:
public void ConfigureServices(IServiceCollection services)
{
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddDistributedMemoryCache();
services.AddSession(options =>
{
// Set a short timeout for easy testing.
options.IdleTimeout = TimeSpan.FromSeconds(1200);
options.Cookie.HttpOnly = true;
});
services.AddHttpContextAccessor();
services.TryAddSingleton<IActionContextAccessor, ActionContextAccessor>();
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddTransient<IClientDataHandler, ClientDataHandler>();
services.AddAuthentication(options => .AddOpenIdConnect("oidc", options =>
{
...
options.Events.OnTokenValidated = async x =>
{
var serviceScopeFactory = services.BuildServiceProvider().GetRequiredService<IServiceScopeFactory>();
...
await x.HttpContext.Session.LoadAsync(new CancellationToken()); --does NOT work
x.HttpContext.Session.Set("clients", Utils.ObjectToByteArray(someData)); --does NOT work
};}
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.UseSession();
app.UseCookiePolicy();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Though this lets me use HttpContext.Session (by injecting IHttpContextAccessor) in any controller or service, I can't use the Session in TokenValidated event handler. Any help?
Thanks in advance.
You should not be building the service provider in your event handler. This is not executed during startup. It's executed on each request by your authentication handler long after the service provider has been built.
options.Events.OnTokenValidated = async context =>
{
// don't do this...service provider is already built
var serviceScopeFactory = services.BuildServiceProvider().GetRequiredService<IServiceScopeFactory>();
};
Instead, you can access the built service provider from the HttpContext.RequestServices.
options.Events.OnTokenValidated = async context =>
{
var serviceScopeFactory = context.HttpContext.RequestServices.GetRequiredService<IServiceScopeFactory>();
};