1

Im struggling to authenticate the current User for my .net core site using CookieAuthentication. After logging in i'm not being redirected to any url, and i'm still on the login form. When debugging i can see that my User is still not authenticated and i get a '302 found'(?) if i navigate to my "authtorized" controller.

I have the following setup in startup.cs.

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole(Configuration.GetSection("Logging"));
            loggerFactory.AddDebug();

            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
                app.UseBrowserLink();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }

            app.UseStaticFiles(new StaticFileOptions
            {
                OnPrepareResponse = ctx =>
                {
                    const int durationInSeconds = 60 * 60 * 24;
                    ctx.Context.Response.Headers[HeaderNames.CacheControl] =
                        "public,max-age=" + durationInSeconds;
                }
            });
            app.UseCookieAuthentication(new CookieAuthenticationOptions()
            {
                AuthenticationScheme = "myCustomScheme",
                LoginPath = new PathString("/Account/Unauthorized/"),
                AccessDeniedPath = new PathString("/Account/Forbidden/"),
                AutomaticAuthenticate = true,
                AutomaticChallenge = true,
                CookieSecure = env.IsDevelopment() ? CookieSecurePolicy.SameAsRequest : CookieSecurePolicy.Always
            });
            app.UseMvc(routes =>
            {

                routes.MapRoute(
                    name: "default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }

AdminController.cs My protected controller(im unsure if i need to specify the scheme)

 [Authorize(ActiveAuthenticationSchemes = "myCustomScheme")]
    public class AdminController : Controller
    {
        public IActionResult Index()
        {
            return View();
        }
    }

AccountController:

[HttpPost]
        public async Task<IActionResult> Unauthorized(LoginModel model, string ReturnUrl)
        {
            if (ModelState.IsValid)
            {
                if (model.Username.ToLower() == "test" && model.Password == "test")
                {
                    var principal = User as ClaimsPrincipal;
                    await HttpContext.Authentication.SignInAsync("myCustomScheme", principal, new AuthenticationProperties
                    {
                        IsPersistent = true,
                    });

                    return RedirectToAction(nameof(AdminController.Index));

                }
                return View(model);
            }
            return View(model);

        }

2 Answers 2

3

In Unauthorized action method you do not have claims in this.User. Instead of

var principal = User as ClaimsPrincipal;

you need to create new identity with own claims and pass it into SignIn method:

var principal = new ClaimsPrincipal(new ClaimsIdentity(
           new[] { new Claim(ClaimTypes.Name, model.Username) },
           "myCustomScheme"));

await HttpContext.Authentication.SignInAsync("myCustomScheme", principal, new AuthenticationProperties
                {
                    IsPersistent = true,
                });

Good simple sample about how to use cookies could be found in aspnet/Security repo

Sign up to request clarification or add additional context in comments.

Comments

0

You would also need to configure all the policies in your Startup.cs under ConfigureServices:

        public void ConfigureServices(IServiceCollection services)
        {
            // Add framework services.
            services.AddDbContext<ApplicationDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));

            services.AddIdentity<ApplicationUser, IdentityRole>()
                .AddEntityFrameworkStores<ApplicationDbContext>()
                .AddDefaultTokenProviders();

            services.AddMvc();

            // some samples (this section must contain all the authorization policies used anywhere in the application)
            services.AddAuthorization(options => {
                options.AddPolicy("EmployeeOnly", policy => policy.RequireClaim("CompanyName", "YourCompany"));
                options.AddPolicy("SalesOnly", policy => { policy.RequireClaim("department", "sales"); });
                options.AddPolicy("HumanResources", policy => { policy.RequireClaim("department", "HR"); });
                options.AddPolicy("FinanceSupervisor", policy => {
                    policy.RequireClaim("department", "finance");
                    policy.RequireClaim("jobTitle", "supervisor");
                });
            });


            // Add application services.
            services.AddTransient<IEmailSender, AuthMessageSender>();
            services.AddTransient<ISmsSender, AuthMessageSender>();
        }

The rest is pretty much the same (here is sample just to make it work with the above):

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        app.UseCookieAuthentication(new CookieAuthenticationOptions()
        {
            AuthenticationScheme = "yourcookiename",                    
            CookieName = "YourCookieName",
            LoginPath = new PathString("/Account/Login"),
            AccessDeniedPath = new PathString("/Account/AccessDenied"),
            AutomaticAuthenticate = true,
            AutomaticChallenge = true
        });

        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        if (env.IsDevelopment()) {
            app.UseDeveloperExceptionPage(); app.UseDatabaseErrorPage(); app.UseBrowserLink();
        } else { app.UseExceptionHandler("/Home/Error"); }

        app.UseStaticFiles();
        app.UseIdentity();

        // Add external authentication middleware below. To configure them please see https://go.microsoft.com/fwlink/?LinkID=532715

        app.UseMvc(routes => {
            routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}");
        });

    }

Under your AccountController.cs in constructor add your db context to pull policies from database

    private readonly YourDB_Context _yourDB_context; 

    public AccountController(YourDB_Context context)
    {
        _yourDB_context = context;
    }

under login add

    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Login(LoginViewModel model, string returnUrl = null)
    {
        ViewData["ReturnUrl"] = returnUrl;
        if (ModelState.IsValid)
        {                
            // modify below to match your user table structure to pull user info
            userTable vUser = _yourDB_Context.userTable.SingleOrDefault(m => m.Email == model.Email && m.password == model.Password);
            const string Issuer = "optional: company name / issuer name";
            List<Claim> claims = new List<Claim> {
                new Claim("CompanyName", "YourCompany"), // hardcoded to authorize EmployeeOnly
                //new Claim("department", "HR"),
                //new Claim(ClaimTypes.Name, vUser.Name, ClaimValueTypes.String, Issuer),
                new Claim(ClaimTypes.Email, vUser.Email, ClaimValueTypes.String, Issuer),
                //new Claim(ClaimTypes.Role, vUser.Roles, ClaimValueTypes.String, Issuer)
            };
            var userIdentity = new ClaimsIdentity(claims, "local", "name", "role");
            var userPrincipal = new ClaimsPrincipal(userIdentity);
            await HttpContext.Authentication.SignInAsync("yourcookiename", userPrincipal,
                new AuthenticationProperties {
                    ExpiresUtc = DateTime.UtcNow.AddMinutes(30),
                    IsPersistent = false,
                    AllowRefresh = false
                });
            return RedirectToLocal(returnUrl);                
        }

        return View(model);
    }

Then under your controller in respetive sections require authorization

[Authorize(Policy = "EmployeeOnly")]
public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }

    [Authorize(Policy = "HumanResources")]
    public IActionResult Contact()
    {
        ViewData["Message"] = "Your contact page.";

        return View();
    }

}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.