I'm having an issue where after creating a user and signing in the SignInManager.IsSignedIn(User)
method returns false
.
I'm using the new 2.0.0 preview of the SPA Angular CLI template and I've added all the identity config based on what I've read in the docs and using some of the other Visual Studio templates as a guide.
Below is my Startup
class.
public class Startup
{
public Startup(IConfiguration configuration, IHostingEnvironment hostingEnvironment)
{
Configuration = configuration;
HostingEnvironment = hostingEnvironment;
//configration
MyAppConfig.ConnectionString = Configuration.GetConnectionString("DefaultConnection");
MyAppConfig.Secure = Configuration.GetValue<bool>("App:Secure", true);
}
public IConfiguration Configuration { get; }
private IHostingEnvironment HostingEnvironment { get; set; }
public IContainer ApplicationContainer { get; private set; }
private const string EmailConfirmationTokenProviderName = "ConfirmEmail";
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
services.AddMvc();
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration =>
{
configuration.RootPath = "ClientApp/dist";
});
IDataProtectionBuilder dataProtectionBuilder = services.AddDataProtection()
.SetApplicationName("MyApp");
//data protection
if (HostingEnvironment.IsDevelopment())
{
//for development, do default (intentionally left blank for now)
}
else
{
//for deployments, protect certificate and persist to azure storage
//this will allow swapping of web app slots
dataProtectionBuilder
.ProtectKeysWithCertificate("")
.PersistKeysToAzureBlobStorage(new Uri(""));
}
//ssl
if (!HostingEnvironment.IsDevelopment())
{
services.Configure<MvcOptions>(options =>
{
options.Filters.Add(new RequireHttpsAttribute());
});
}
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
//anti-forgery, add to all controllers
services.AddMvc(options => {
options.Filters.Add(new ValidateAntiForgeryTokenAttribute());
options.Filters.Add(new AuthorizeFilter(policy));
});
services.AddAntiforgery(options => options.HeaderName = "X-XSRF-TOKEN");
services.AddDbContext<AppDbContext>(options =>
options.UseSqlServer(MyAppConfig.ConnectionString));
//identity
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders()
.AddTokenProvider<ConfirmEmailDataProtectorTokenProvider<ApplicationUser>>(EmailConfirmationTokenProviderName); ;
services.Configure<IdentityOptions>(options =>
{
// Password settings
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = false;
options.Password.RequiredUniqueChars = 4;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings
options.User.RequireUniqueEmail = true;
// SignIn settings
options.SignIn.RequireConfirmedEmail = true;
options.SignIn.RequireConfirmedPhoneNumber = false;
options.Tokens.EmailConfirmationTokenProvider = EmailConfirmationTokenProviderName;
});
services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.Cookie.Expiration = TimeSpan.FromHours(1);
options.SlidingExpiration = true;
});
services.Configure<ConfirmEmailDataProtectionTokenProviderOptions>(options =>
{
options.TokenLifespan = TimeSpan.FromDays(180);
});
//autofac
var builder = new ContainerBuilder();
builder.Populate(services);
builder.RegisterType<AccountService>().As<IAccountService>();
builder.RegisterType<EmailService>().As<IEmailService>();
this.ApplicationContainer = builder.Build();
// Create the IServiceProvider based on the container.
return new AutofacServiceProvider(this.ApplicationContainer);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IAntiforgery antiforgery)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseSpaStaticFiles();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
//send anti-forgery cookie with initial SPA page
//must be before UseSpa() call
app.Use(next => context =>
{
if (
string.Equals(context.Request.Path.Value, "/", StringComparison.OrdinalIgnoreCase) ||
string.Equals(context.Request.Path.Value, "/index.html", StringComparison.OrdinalIgnoreCase))
{
// We can send the request token as a JavaScript-readable cookie, and Angular will use it by default.
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
new CookieOptions() { HttpOnly = false });
}
return next(context);
});
app.UseSpa(spa =>
{
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment())
{
spa.UseAngularCliServer(npmScript: "start");
//spa.UseProxyToSpaDevelopmentServer("http://localhost:4200");
}
});
//auth & identity
app.UseAuthentication();
}
}
Here are the steps I follow to test if the user gets signed in:
SignInManager.PasswordSignInAsync()
in a new HTTP request which returns SuccessSignInManager.IsSignedIn(User)
and it returns false
.I'm not sure what the issue might be as I've read through the documentation at https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity?tabs=visual-studio%2Caspnetcore2x
Your Middleware executes in the order you declare things in your app. Here is a great resource to help you understand better,
when you call authentication last, this will be the last thing done in the Pipeline:
app.UseAuthentication();
To fix this just put this line above the:
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller}/{action=Index}/{id?}");
});
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With