locked
ASP.NET MVC Authentication and Authorization RRS feed

  • Question

  • User-1635004321 posted

    Hello , 

    I have been trying to implement Custom Authentication and Authorization in my project. However i have come across a problem where even debugging doesn't help. My application on authenticating a user after a sign in shows menu items based on user roles. However after signing in and some few requests served while clicking different menu items of different controllers and actions. The user is invalidated and the menu items for this role are gone. Only the static items remain. please help identifying this problem 

    here is the Authorize Attribute code.

     public class OAuthorizeAttribute : AuthorizeAttribute
        {
            bool authorized = false;
    
            public OAuthorizeAttribute(params UserRoles[] _Roles) => Roles = string.Join(",", _Roles.Select(s => s.GetEnumDescription())) ?? null;
    
            protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
            {
    
                if (!filterContext.HttpContext.User.Identity.IsAuthenticated && !authorized)
                {
                    base.HandleUnauthorizedRequest(filterContext);
                    UrlHelper urlHelper = new UrlHelper(filterContext.RequestContext);
                    filterContext.HttpContext.Response.Redirect(urlHelper.Action("SignIn", "Account", new { area = string.Empty, R = HttpContext.Current.Request.RawUrl }), true);
                }
                else
                {
                    UrlHelper urlHelper = new UrlHelper(filterContext.RequestContext);
                    filterContext.HttpContext.Response.Redirect(urlHelper.Action("Forbidden", "Account", new { area = string.Empty }), true);
                }
            }
    
            protected override bool AuthorizeCore(HttpContextBase httpContext)
            {
                try
                {
                    HttpCookie authCookie = httpContext.Request.Cookies[AuthCookie.Name];
                    if (authCookie != null)
                    {
                        FormsAuthenticationTicket authTicket = FormsAuthentication.Decrypt(authCookie.Value);
                        AppUser serializeModel = new JavaScriptSerializer().Deserialize<AppUser>(authTicket.UserData.DecryptCookieAndURLSafe());
    
                        PrincipalProvider _AppUser = new PrincipalProvider(serializeModel, serializeModel.FullName);
    
                        if (_AppUser.IsInRole(Roles))
                        {
                            HttpContext.Current.User = _AppUser;
                            authorized = true;
                            return true;
                        }
                        else
                        {
                            authorized = false;
                            HttpContext.Current.User = null;
                            return false;
                        }                    
                    }
                }
                catch (CryptographicException)
                {
                    new BusinessManagers.AuthenticationManager().SignOut();
                }
                return authorized;
            }
    
        }

    Given Below is the Code for menu item generation.

     @if (User.IsInRole(Library.Models.UserRoles.Administrator.ToString()))
    
    {
        <li>
            <a href="@Url.Action("CreateUser","Account")">Issued Books</a>
         </li>
     }

    Part of the Controller Code

     [OAuthorize(UserRoles.Administrator)]
        public class BooksController : BaseController
        {
            [AllowAnonymous]
            public ActionResult Books(string AuthorNameOrTitle)
            {
                FillViewBag_Users();
                TempData["ViewBag.AuthorNameOrTitle"] = AuthorNameOrTitle;
                return View();
            }
            [AllowAnonymous]
            public ActionResult BooksGrid()
            {
                FillViewBag_Users();
                List<Book> books = new BooksManager().Books();
                return View(books);
            }
       }


    Question 1 : The web.config doesn't include Forms tag. because other application is working absolutely fine as expected. is that mandatory ?

    Question 2 : The Cookie seems to exist on the browser and the logout button is still there (which is there only when the user is logged in) so i guess it isn't an authentication problem. If this is an authorization problem. which part seems to you as incorrect. ?

    Thanks.

    Monday, December 10, 2018 7:05 PM

All replies

  • User-1635004321 posted
    Anyone ?
    Wednesday, December 12, 2018 12:22 PM
  • User753101303 posted

    Hi,

    It's a bit hard to try to help on a custom system when not knowing much about what actually happens and the underlying design (also it's vague ie what is an "invalidated" user, it seems you tell the user is still authenticated). Why debugging doesn't help ? The code path is really always the same even when the problem happens ??? Signout is not called ? You have a single server ?

    My first move would be likely to create a test page that dumps user details and more (such as the server name if load balancing is used) to see what happens. For now my understanding is that the user is still authenticated but is not any more in some roles (that are coming from ?)

    BTW you really can't build on what ASP.NET offers out of the box or at least start from that ?

    Wednesday, December 12, 2018 12:47 PM
  • User475983607 posted

    Use Cookie Authentication rather than Forms Authentication.

    https://docs.microsoft.com/en-us/aspnet/core/security/authentication/cookie?view=aspnetcore-2.2

    Wednesday, December 12, 2018 1:11 PM
  • User-1635004321 posted

    Thank you for the reply. 

    ignout is not called ?
    My first move would be likely to create a test page that dumps user details

    The breakpoint is never hit. The cookie exists in the browser and is not removed. however, the request object shows the request is not authenticated. Given below is a piece of code.

    @if (Request.IsAuthenticated || User.Identity.IsAuthenticated || Request.Cookies[Library.Models.AuthCookie.Name] != null)
                    {
                        <a class="btn btn-outline-success" href="@Url.Action("LogOut","Account")"><i class="fa fa-user"></i> Logout</a>
                    }
                    else
                    {
                        <a class="btn btn-outline-primary" href="@Url.Action("Login","Account")"><i class="fa fa-user"></i> Login</a>
                    }

    The Request above shows User.Identity.IsAuthenticated is false. Also, the request doesn't carry the Custom IPrincipal object filled during the signing in. 

    it seems you tell the user is still authenticated

    I say this because the cookie is still there. 

    My first move would be likely to create a test page that dumps user details
    <g class="gr_ gr_1150 gr-alert gr_tiny gr_spell gr_inline_cards gr_run_anim ContextualSpelling multiReplace" id="1150" data-gr-id="1150">i</g> don't have the user details at that time. If you need more information you can tell me what you want <g class="gr_ gr_1220 gr-alert gr_tiny gr_spell gr_inline_cards gr_run_anim ContextualSpelling multiReplace" id="1220" data-gr-id="1220">i</g> will reply with code <g class="gr_ gr_1221 gr-alert gr_tiny gr_spell gr_inline_cards gr_run_anim ContextualSpelling multiReplace" id="1221" data-gr-id="1221">i</g> have used.

    BTW you really can't build on what ASP.NET offers out of the box or at least start from <g class="gr_ gr_1229 gr-alert gr_gramm gr_inline_cards gr_run_anim Style multiReplace" id="1229" data-gr-id="1229">that ?</g>

    I didn't exactly get this.

    Thanks.

    Wednesday, December 12, 2018 2:37 PM
  • User-1635004321 posted

    Thank you for your reply.

    mgebhard

    Use Cookie Authentication rather than Forms Authentication.

    The link seems for asp.net core. it would work for me as well? mine isn't a CORE project.

    Wednesday, December 12, 2018 2:40 PM
  • User475983607 posted

    The link seems for asp.net core. it would work for me as well? mine isn't a CORE project.

    Then use the OWIN auth cookie.

    https://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/

    https://weblog.west-wind.com/posts/2015/Apr/29/Adding-minimal-OWIN-Identity-Authentication-to-an-Existing-ASPNET-MVC-Application

    Question 1 : The web.config doesn't include Forms tag. because other application is working absolutely fine as expected. is that mandatory ?

    Yes, if you want forms authentication to start up.  Forms authentication reads the cookie on each request and sets up the principal.  You'll need code in the Global.asax if you do not turn on forms authentication.

    Question 2 : The Cookie seems to exist on the browser and the logout button is still there (which is there only when the user is logged in) so i guess it isn't an authentication problem. If this is an authorization problem. which part seems to you as incorrect. ?

    There is no way for us to verify.

    Wednesday, December 12, 2018 2:58 PM
  • User753101303 posted

    Ok so ti seems you don"t really have to roll your own ? You are storing accounts in your SQL Server database ? For example you could create a new project and use https://docs.microsoft.com/en-us/aspnet/visual-studio/overview/2013/creating-web-projects-in-visual-studio#indauth

    Does it work fine ? Does it fit your needs ?

    Wednesday, December 12, 2018 3:17 PM
  • User-1635004321 posted

    Again, Thanks for your reply.

    I have kind of figured it out after a lot of debugging. Here is the scenario. 

    1. I have controllers decorated with [OAuthorize(UserRoles.Administrator)]. 

    2. Some of the actions of these Controllers are the menu items I show in the view. The Menu Bar. The Menu Bar decides the menu items to be displayed after a check of the user role of the user. It then decides if it is to be shown to this user.

    3. Now, Some of the actions of these controllers are visible to the public. These are the menu items (links) that should be available to the public. These actions are decorated with [AllowAnonymous].

    4. After logging in to the administrator account, All the action links ( Menu Items ) are visible ( AllowAnonymous and Administrator links).

    5. After I clicked a link of a controller which was not decorated with [OAuthorize(UserRoles.Administrator)] the Request.IsAuthenticated was false and the principal lost. No  call was made to AuthorizeCore() OR HandleUnauthorizedRequest() afterwards. The  Administrator Menu Items disappeared but the logout button was there since the cookie existed.

    6. I fixed this by decorating the controller with OAuthorize(UserRoles.Administrator)].

    Now please help me understand why did the Request.IsAuthenticated <g class="gr_ gr_35 gr-alert gr_gramm gr_inline_cards gr_run_anim Grammar multiReplace" id="35" data-gr-id="35">turn</g> false if the controller wasn't decorated with [OAuthorize(UserRoles.Administrator)]. The request was authentic but not authorized to view that action. As per the HandleUnauthorizedRequest() code given above it should have been properly redirected to a Forbidden Page.

    Thanks

    Wednesday, December 12, 2018 4:35 PM
  • User-1635004321 posted

    <g class="gr_ gr_5 gr-alert gr_gramm gr_inline_cards gr_run_anim Punctuation only-ins replaceWithoutSep" id="5" data-gr-id="5">Thanks</g> PatriceSc. Please check my last reply to mgebhard

    Wednesday, December 12, 2018 4:36 PM
  • User475983607 posted

    Again, Thanks for your reply.

    I have kind of figured it out after a lot of debugging. Here is the scenario. 

    1. I have controllers decorated with [OAuthorize(UserRoles.Administrator)]. 
    2. Some of The actions of these Controllers are the menu items I show in the view. The Menu Bar. The Menu Bar decides the menu items to be displayed after a check of the user role if it is to be allowed to be shown to this user. so far so good? 
    3. Now, Some of the actions of these controllers are visible to the public. The menu items (links) that should be available to the public. These actions are decorated with [AllowAnonymous].
    4. After logging in to the administrator account, All the action links ( Menu Items ) are visible ( AllowAnonymous and Administrator links).
    5. After I clicked a link of a controller which was not decorated with [OAuthorize(UserRoles.Administrator)] the Request.IsAuthenticated was false and the principal lost. No action call called AuthorizeCore() OR HandleUnauthorizedRequest(). The  Administrator Menu Items disappear but the logout button is there since the cookie exists.
    6. I fixed this by decorating the controller with OAuthorize(UserRoles.Administrator)].

    Now please help me understand why did the Request.IsAuthenticated turn false if the controller wasn't decorated with [OAuthorize(UserRoles.Administrator)]. The request was authentic but not authorized to view that action. As per the HandleUnauthorizedRequest() code given above it should have been properly redirected to a Forbidden Page.

    Thanks

    The code is functioning exactly as written.  All the authorization logic is within the OAuthorize attribute.  The Principal should be populated in the Global.asax on every request.  The OAuthorize attribute simply reads the Principal. 

    However, I strongly recommend using the framework APIs rather than rolling your own.  The APIs have been tested for years and are known to work properly.

    Wednesday, December 12, 2018 4:48 PM
  • User-1635004321 posted

    if I turn on the FormsAuthentication in the web.config. that should suffice. isn't it ? 

    Wednesday, December 12, 2018 4:52 PM
  • User475983607 posted

    fahrabh

    if I turn on the FormsAuthentication in the web.config. that should suffice. isn't it ? 

    No, forms authentication does not cache the user's roles.  You'll need to write code in the Global.asax to either look up the roles in the DB or fetch the roles from a custom token within the form auth cookie "User Data" and populate the Principal.  That should allow you to use the standard [Authorize] attribute.

    I look into OWIN Cookie auth as this logic is built-in.

    Wednesday, December 12, 2018 5:19 PM
  • User-1635004321 posted

    I would use OWIN Cookie auth as it already has this logic built-in.

    Please help with any link available for how this is implemented. 

    Wednesday, December 12, 2018 5:22 PM
  • Wednesday, December 12, 2018 5:27 PM