locked
Authorize not Securing Controller RRS feed

  • Question

  • User1122355199 posted

    Hello everyone and thanks for the help in advance.  I'm upgrading some applications to .Net 5 and am working through the article at https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-5.0.  I have a very simple controller that looks like:

            [Authorize]
            public IActionResult Secured()
            {
                return View();
            }

    However, I am able to browse to this page without authentication.  In my startup, I ahve:

                services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options => {
    
                    options.LoginPath = @"/Auth/Login";
                    options.Cookie.Name = "mycookie";
                
                });

    In Configure:

                app.UseAuthentication();
                app.UseAuthorization();

    I know these are order specific, but I think it is set up correctly.  So at this point, I'm thinking cookie authentication is set up and a redirect should happen to the login page, but that doesn't work.  Any help would be appreciated.

    Sunday, December 13, 2020 3:43 AM

Answers

  • User475983607 posted

    The usual issue is the developer does not realize a persistent cookie already exists or there is a configuration issue.   Open the browser's dev tools to see if the cookie "mycookie" exists.  Unfortunately, the original code is incomplete.  The community cannot accurately review the code for mistakes.

    I can assure you the linked documentation work as expected.  The only other time I see the [Authorize] attribute allowing access is when there are multiple authentication schemes that are not configured correctly.

    I created a test and cannot reproduce your findings.  Below is my test code copied directly from the linked documentation and your configuration.

    using Microsoft.AspNetCore.Authentication.Cookies;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.HttpsPolicy;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace MvcDemo
    {
        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.AddControllersWithViews();
    
                services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options => {
                    options.LoginPath = @"/Auth/Login";
                    options.Cookie.Name = "mycookie";
                });
            }
    
            // 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");
                    // 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.UseRouting();
    
                app.UseAuthentication();
                app.UseAuthorization();
               
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllerRoute(
                        name: "default",
                        pattern: "{controller=Home}/{action=Index}/{id?}");
                });
            }
        }
    }
    
       public class HomeController : Controller
        {
            private readonly ILogger<HomeController> _logger;
    
            public HomeController(ILogger<HomeController> logger)
            {
                _logger = logger;
            }
    
            public IActionResult Index()
            {
                return View();
            }
    
            [Authorize]
            public IActionResult Secured()
            {
                return View();
            }
    
            public IActionResult Privacy()
            {
                return View();
            }
    
            [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
            public IActionResult Error()
            {
                return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
            }
        }
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Authentication.Cookies;
    using Microsoft.AspNetCore.Mvc;
    using MvcDemo.Models;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Claims;
    using System.Threading.Tasks;
    
    namespace MvcDemo.Controllers
    {
        public class AuthController : Controller
        {
            [HttpGet]
            public IActionResult login(string ReturnUrl)
            {
                ViewBag.ReturnUrl = ReturnUrl;
                return View();
            }
            [HttpPost]
            public async Task<IActionResult> loginAsync(UserLogin user, string ReturnUrl)
            {
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, user.UserName),
                    new Claim(ClaimTypes.Role, "Administrator"),
                };
    
                var claimsIdentity = new ClaimsIdentity(
                    claims, CookieAuthenticationDefaults.AuthenticationScheme);
    
                var authProperties = new AuthenticationProperties
                {
                    //AllowRefresh = <bool>,
                    // Refreshing the authentication session should be allowed.
    
                    //ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10),
                    // The time at which the authentication ticket expires. A 
                    // value set here overrides the ExpireTimeSpan option of 
                    // CookieAuthenticationOptions set with AddCookie.
    
                    //IsPersistent = true,
                    // Whether the authentication session is persisted across 
                    // multiple requests. When used with cookies, controls
                    // whether the cookie's lifetime is absolute (matching the
                    // lifetime of the authentication ticket) or session-based.
    
                    //IssuedUtc = <DateTimeOffset>,
                    // The time at which the authentication ticket was issued.
    
                    //RedirectUri = <string>
                    // The full path or absolute URI to be used as an http 
                    // redirect response value.
                };
    
                await HttpContext.SignInAsync(
                    CookieAuthenticationDefaults.AuthenticationScheme,
                    new ClaimsPrincipal(claimsIdentity),
                    authProperties);
    
                return Redirect(ReturnUrl);
            }
    
    
        }
    }
    
        public class UserLogin
        {
            public string UserName { get; set; }
            public string Password { get; set; }
        }
    @model MvcDemo.Models.UserLogin
    
    @{
        ViewData["Title"] = "login";
    }
    
    <h1>login</h1>
    
    <h4>UserLogin</h4>
    <hr />
    <div class="row">
        <div class="col-md-4">
            <form asp-action="login">
                <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                <div class="form-group">
                    <label asp-for="UserName" class="control-label"></label>
                    <input asp-for="UserName" class="form-control" value="HelloWorld" />
                    <span asp-validation-for="UserName" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <label asp-for="Password" class="control-label"></label>
                    <input asp-for="Password" class="form-control" value="password" />
                    <span asp-validation-for="Password" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <input type="hidden" name="ReturnUrl" value="@ViewBag.ReturnUrl" />
                    <input type="submit" value="Create" class="btn btn-primary" />
                </div>
            </form>
        </div>
    </div>

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Sunday, December 13, 2020 1:03 PM

All replies

  • User475983607 posted

    The usual issue is the developer does not realize a persistent cookie already exists or there is a configuration issue.   Open the browser's dev tools to see if the cookie "mycookie" exists.  Unfortunately, the original code is incomplete.  The community cannot accurately review the code for mistakes.

    I can assure you the linked documentation work as expected.  The only other time I see the [Authorize] attribute allowing access is when there are multiple authentication schemes that are not configured correctly.

    I created a test and cannot reproduce your findings.  Below is my test code copied directly from the linked documentation and your configuration.

    using Microsoft.AspNetCore.Authentication.Cookies;
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.AspNetCore.HttpsPolicy;
    using Microsoft.Extensions.Configuration;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Threading.Tasks;
    
    namespace MvcDemo
    {
        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.AddControllersWithViews();
    
                services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options => {
                    options.LoginPath = @"/Auth/Login";
                    options.Cookie.Name = "mycookie";
                });
            }
    
            // 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");
                    // 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.UseRouting();
    
                app.UseAuthentication();
                app.UseAuthorization();
               
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapControllerRoute(
                        name: "default",
                        pattern: "{controller=Home}/{action=Index}/{id?}");
                });
            }
        }
    }
    
       public class HomeController : Controller
        {
            private readonly ILogger<HomeController> _logger;
    
            public HomeController(ILogger<HomeController> logger)
            {
                _logger = logger;
            }
    
            public IActionResult Index()
            {
                return View();
            }
    
            [Authorize]
            public IActionResult Secured()
            {
                return View();
            }
    
            public IActionResult Privacy()
            {
                return View();
            }
    
            [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
            public IActionResult Error()
            {
                return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
            }
        }
    using Microsoft.AspNetCore.Authentication;
    using Microsoft.AspNetCore.Authentication.Cookies;
    using Microsoft.AspNetCore.Mvc;
    using MvcDemo.Models;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Security.Claims;
    using System.Threading.Tasks;
    
    namespace MvcDemo.Controllers
    {
        public class AuthController : Controller
        {
            [HttpGet]
            public IActionResult login(string ReturnUrl)
            {
                ViewBag.ReturnUrl = ReturnUrl;
                return View();
            }
            [HttpPost]
            public async Task<IActionResult> loginAsync(UserLogin user, string ReturnUrl)
            {
                var claims = new List<Claim>
                {
                    new Claim(ClaimTypes.Name, user.UserName),
                    new Claim(ClaimTypes.Role, "Administrator"),
                };
    
                var claimsIdentity = new ClaimsIdentity(
                    claims, CookieAuthenticationDefaults.AuthenticationScheme);
    
                var authProperties = new AuthenticationProperties
                {
                    //AllowRefresh = <bool>,
                    // Refreshing the authentication session should be allowed.
    
                    //ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(10),
                    // The time at which the authentication ticket expires. A 
                    // value set here overrides the ExpireTimeSpan option of 
                    // CookieAuthenticationOptions set with AddCookie.
    
                    //IsPersistent = true,
                    // Whether the authentication session is persisted across 
                    // multiple requests. When used with cookies, controls
                    // whether the cookie's lifetime is absolute (matching the
                    // lifetime of the authentication ticket) or session-based.
    
                    //IssuedUtc = <DateTimeOffset>,
                    // The time at which the authentication ticket was issued.
    
                    //RedirectUri = <string>
                    // The full path or absolute URI to be used as an http 
                    // redirect response value.
                };
    
                await HttpContext.SignInAsync(
                    CookieAuthenticationDefaults.AuthenticationScheme,
                    new ClaimsPrincipal(claimsIdentity),
                    authProperties);
    
                return Redirect(ReturnUrl);
            }
    
    
        }
    }
    
        public class UserLogin
        {
            public string UserName { get; set; }
            public string Password { get; set; }
        }
    @model MvcDemo.Models.UserLogin
    
    @{
        ViewData["Title"] = "login";
    }
    
    <h1>login</h1>
    
    <h4>UserLogin</h4>
    <hr />
    <div class="row">
        <div class="col-md-4">
            <form asp-action="login">
                <div asp-validation-summary="ModelOnly" class="text-danger"></div>
                <div class="form-group">
                    <label asp-for="UserName" class="control-label"></label>
                    <input asp-for="UserName" class="form-control" value="HelloWorld" />
                    <span asp-validation-for="UserName" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <label asp-for="Password" class="control-label"></label>
                    <input asp-for="Password" class="form-control" value="password" />
                    <span asp-validation-for="Password" class="text-danger"></span>
                </div>
                <div class="form-group">
                    <input type="hidden" name="ReturnUrl" value="@ViewBag.ReturnUrl" />
                    <input type="submit" value="Create" class="btn btn-primary" />
                </div>
            </form>
        </div>
    </div>

    • Marked as answer by Anonymous Thursday, October 7, 2021 12:00 AM
    Sunday, December 13, 2020 1:03 PM
  • User753101303 posted

    Hi,

    And/or inspect https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.mvc.controllerbase.user?view=aspnetcore-5.0 starting maybe with User.Identity.IsAuthenticated and User.Identity.AuthenticationType. Could it be that Windows authentication is enabled on the IIS side?

    Sunday, December 13, 2020 1:46 PM
  • User1122355199 posted

    Thanks so much for the response and, as always, your advice was great.  I finally isolated the problem to a separate test web application that was setting a cookie that didn't expire.  As you suggested, the console showed the cookie which was not set by the current application., but were using the same test name.  So reconfiguring the apps cookie name and expiration solved the problem.  Thanks again for pointing me in the right direction.

    Tuesday, December 15, 2020 8:47 PM