skip to Main Content

I am adding a custom code to AuthorizeAttribute that routes a user to an action in another controller that displays a modal popup when they have not verified their phone number.

For some reason, the code just redirects forever that the web site just says:

redirected you too many times

And does not hit my breakpoint that I put in the VerifyPhoneNumber Action.

Can you suggest why this happens? TIA.

Here is my custom overridden AuthorizeAttribute class far below. Everything here has been here before, I just added:

  if (CustomContext.AccountDomainType == AccountDomainType.ActiveDirectory &&
                    CustomContext.IsPhoneNumberConfirmed == false)
                {
                    return false;
                }

And:

if (CustomContext.AccountDomainType == AccountDomainType.ActiveDirectory &&
                    CustomContext.IsPhoneNumberConfirmed == false)
                {
                    filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary
                    {
                        { "action", "VerifyPhoneNumber" },
                        { "controller", "Account" }
                    });

                    return;
                }

CustomAuthorizeAttribute class:

public class CustomAuthorizeAttribute : AuthorizeAttribute
    {
        public Module Module { get; set; }
        public Permission Permission { get; set; }

        protected override bool AuthorizeCore(HttpContextBase httpContext)
        {
            var CustomContext = (Context)httpContext.Items[Context.HttpContextItemName];
            var token = httpContext.Request.Form["token"];
            if (!base.AuthorizeCore(httpContext) && token == null)
            {
                return false;
            }
            else if (token != null)
            {
                var tokenRepository = new AuthenticationTokenRepository(CustomContext);
                var authenticationToken = tokenRepository.Get(token);
                if (authenticationToken == null)
                {
                    CustomContext.Log("Permission denied because authentication token was not valid");
                    return false;
                }
                var authenticationManager = httpContext.GetOwinContext().Authentication;
                var userManager = httpContext.GetOwinContext().Get<AspNetUserManager>();
                AspNetUser user;
                if (authenticationToken.Role == AuthenticationTokenRole.PdfGenerator)
                {
                    user = userManager.FindByName<AspNetUser, Guid>(authenticationToken.CreatedBy);
                    if (user == null)
                    {
                        CustomContext.Log("Permission denied because token creation user was not found");
                        return false;
                    }
                }
                else
                {
                    return false;
                }

                var userIdentity = ApplicationSignInManager.GenerateUserIdentity(userManager, user, DefaultAuthenticationTypes.ApplicationCookie);
                authenticationManager.SignIn(new AuthenticationProperties { IsPersistent = false }, userIdentity);
                if (!authenticationManager.User.Identity.IsAuthenticated)
                {
                    authenticationManager.User = authenticationManager.AuthenticationResponseGrant.Principal;
                }
            }
       
            if (token == null && !CustomContext.IsDeviceAuthorized())
            {
                CustomContext.UpdateDevice();
                httpContext.GetOwinContext().Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie, DefaultAuthenticationTypes.TwoFactorCookie, DefaultAuthenticationTypes.ApplicationCookie);
                CustomContext.Log("Permission denied and forced sign out because device was already signed out based on cookie check");
                return false;
            }
            if (Module != default(Module) && Permission != default(Permission) && !CustomContext.HasPermission(Module, Permission))
            {
                CustomContext.Log("Permission denied because user does not have permission to " + Module.DisplayName() + ", " + Permission.DisplayName());
                return false;
            }
            if (Module != default(Module) && Permission == default(Permission) && !CustomContext.HasModule(Module))
            {
                CustomContext.Log("Permission denied because company does not have the following module features turned on " + Module.DisplayName());
                return false;
            }
            if (CustomContext.IsPasswordExpired
                && httpContext.Request.RequestContext.RouteData.Values.ContainsKey("action")
                && "ChangePassword" != httpContext.Request.RequestContext.RouteData.Values["action"].ToString()
                && "ChangePasswordLink" != httpContext.Request.RequestContext.RouteData.Values["action"].ToString())
            {
                CustomContext.Log("Permission denied because password is expired. Password must be changed first");
                return false;
            }
            if (Module == default(Module) && Permission != default(Permission))
            {
                throw new Exception("No module specified for permission check");
            }
            
            if (CustomContext.AccountDomainType == AccountDomainType.ActiveDirectory &&
                CustomContext.IsPhoneNumberConfirmed == false)
            {
                return false;
            }
            
            return true;



        }

        protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
        {
            if (filterContext.HttpContext.User.Identity.IsAuthenticated)
            {
                var actionParams = filterContext.ActionDescriptor.GetParameters();
                var paramTypes = actionParams.Select(ap => ap.ParameterType).ToArray();
                var actionMethod = filterContext.Controller.GetType()
                    .GetMethod(filterContext.ActionDescriptor.ActionName, paramTypes);
                var CustomContext = (Context)filterContext.HttpContext.Items[Context.HttpContextItemName];
                var errorTypeViewPrefix = CustomContext.IsPasswordExpired ? "PasswordExpired" : "PermissionDenied";

                if (CustomContext.AccountDomainType == AccountDomainType.ActiveDirectory &&
                    CustomContext.IsPhoneNumberConfirmed == false)
                {
                    filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary
                    {
                        { "action", "VerifyPhoneNumber" },
                        { "controller", "Account" }
                    });

                    return;
                }
                if (actionMethod != null && (actionMethod.ReturnType == typeof(PartialViewResult) ||
                                             actionMethod.ReturnType == typeof(Task<PartialViewResult>)))
                    {
                        // Partial views
                        filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary
                        {
                            { "action", errorTypeViewPrefix + "Partial" },
                            { "controller", "Error" }
                        });

                        return;
                    }

                // Full page views
                filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary
                {
                    { "action", errorTypeViewPrefix },
                    { "controller", "Error" }
                });
                return;
            
            }
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}

The action I am trying to execute that sends a text message code to user and opens a popup window (it exists in another controller):

 [Display(Name = "Verify Phone Number", Description = "Verify Phone Number")]
        public async Task<PartialViewResult> VerifyPhoneNumber()
        {
            var strictPhoneAttr = new StrictPhoneAttribute();
            if (!strictPhoneAttr.IsValid(Context.PhoneNumber))
            {
                ModelState.AddModelError(string.Empty, "Please, change your phone number to a valid cell phone number under the menu in the top right-hand corner under your name. A valid cell phone number is required for two-factor authentication used by this system.");
            }
            else
            {
           /....code that send SMS code ......./
            Context.Log("Opened Verify Phone Number modal");
            return PartialView("_VerifyPhoneNumber", new VerifyPhoneNumberModel());
        }

2

Answers


    1. Make sure that when the user is redirected to the VerifyPhoneNumber action, they are not automatically redirected again to the original action (due to the same condition being evaluated), i think you could add some logging to improve dubugging.

    2. You might want to change the logic to update the CustomContext.IsPhoneNumberConfirmed once the phone number is successfully verified, because if the modal does not allow the user to resolve the issue, they will keep being redirected.

    3. You can modify your AuthorizeCore method to check if the current action is VerifyPhoneNumber. If so, you can skip the redirect:

    if (filterContext.ActionDescriptor.ActionName == "VerifyPhoneNumber" && filterContext.ActionDescriptor.ControllerDescriptor.ControllerName == "Account")
    {
        return true;
    }
    
    1. Make sure that in the HandleUnauthorizedRequest method, you are not creating multiple conditions that lead to the same redirect.
    2. Here is a modified part of the AuthorizeCore method that avoids redirect loops:
    if (CustomContext.AccountDomainType == AccountDomainType.ActiveDirectory &&
        CustomContext.IsPhoneNumberConfirmed == false)
    {
        // check if we are already in the VerifyPhoneNumber action to prevent a redirect loop
        if (!(httpContext.Request.RequestContext.RouteData.Values["action"].ToString() == "VerifyPhoneNumber" &&
              httpContext.Request.RequestContext.RouteData.Values["controller"].ToString() == "Account"))
        {
            return false; // triggers redirect logic in HandleUnauthorizedRequest
        }
    }
    
    Login or Signup to reply.
  1. It is not appropriate to use redirect inside AuthorizeAttribute because it is supposed only to check Authorize and return results,and also it may have redirected after checking Authorization.
    why you don’t use: the action filter attribute
    for more instances:
    https://learn.microsoft.com/en-us/aspnet/mvc/overview/older-versions-1/controllers-and-routing/understanding-action-filters-cs

        public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
    string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToString();
    string actionName = filterContext.ActionDescriptor.ActionName.ToString();
    
    //Authentication & Authorization mechanism
    //if fail then I want to redirect to login Controller with parameter '1'
    //I have tried the following methods, both redirects but not pasasing the parameter
    
    //I have tried the following methods, both redirects but not passing the parameter
    //Method 1
    
    var values = new RouteValueDictionary(new { 
    action = "Index",
    controller = "Login",
    code = "1"
    });
    filterContext.Result = new RedirectToRouteResult(values);
    //Method 2 also tried Login/Index?code=1
    filterContext.Result = new RedirectResult("Login/Index/1");
    
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search