1

I want to prevent an view from directly being accessed. I have a view that displays Email Confirmed, when a confirmation link in the users email is clicked. But, the problem is the Url of that view can be directly accessed, I just wan it to be accessed when the link in email is clicked.

I have tried this option, but keep getting error as the UrlReferer is not available in Asp.net core. this code will not work in asp.net core, can someone help me please.

How to access these values in ASp.net Core 2.x :

filterContext.HttpContext.Request.UrlReferrer
filterContext.HttpContext.Request.Url.Host
filterContext.HttpContext.Request.UrlReferrer.Host

Code I found on Stack-overflow - but it needs System.Web, which is not available in asp.net core.


public class NoDirectAccessAttribute : ActionFilterAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        if (filterContext.HttpContext.Request.UrlReferrer == null ||
                    filterContext.HttpContext.Request.Url.Host != filterContext.HttpContext.Request.UrlReferrer.Host)
            {
            filterContext.Result = new RedirectToRouteResult(new
                           RouteValueDictionary(new { controller = "Home", action = "Index", area = "" })); 
        }
    }
}

Recommended way to use the custom Attribute

[NoDirectAccess]
public ActionResult MyActionMethod()

4
  • stackoverflow.com/questions/38772394/… Commented Feb 6, 2019 at 22:55
  • I tried this but always returns null - I need to get it from ActionExecutingContext string referer = context.HttpContext.Request.Headers["Referer"].ToString(); Commented Feb 6, 2019 at 23:03
  • @LyoidLopes Probably you need to refactor the way it validates. UrlReferer is easy to pass by. Also, if the user has confirmed the email, but access the view by pasting the URL directly in the browser twice, how do you determine whether that view should be displayed or not ? IMO, If the user has confirmed the email, simply store the EmailConfrimed=true somewhere (Database/JWT/...). And now you could use a filter/policy to prevent the view from directly being accessed Commented Feb 7, 2019 at 1:35
  • I am trying to, but to stop the user from preview again I need to get UrlReferer. If it is easy can you give me an example of the filter. View is displayed when they click on the email link. but when they copy paste that same link the can still see the email confirmed message. I just want a filter to prevent direct access to url. but for that i needs System.Web - I cannot use it in Asp.net core - therefore, how to I get the value of UrlReferer in the filter. Commented Feb 7, 2019 at 1:47

2 Answers 2

3
  1. If you do need UrlReferer, create a filter as below :
    public class NoDirectAccessAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var canAcess= false;

            // check the refer
            var referer= filterContext.HttpContext.Request.Headers["Referer"].ToString(); 
            if(!string.IsNullOrEmpty(referer)){
                var rUri = new System.UriBuilder(referer).Uri;
                var req = filterContext.HttpContext.Request;
                if(req.Host.Host==rUri.Host && req.Host.Port == rUri.Port && req.Scheme == rUri.Scheme ){
                    canAcess=true;
                }
            }

            // ... check other requirements

            if(!canAcess){
                filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Home", action = "Index", area = "" })); 
            }
        }
    }

and now you can decorate your action method with a [NoDirectAccess] :

        [NoDirectAccess]
        public IActionResult Privacy()
        {
            return View();
        }
  1. It is not suggested to use Referer to authorize request because it's quite easy to fake a HTTP Header of UrlReferer.

IMO, it is much better to store the EmailConfirmed=true somewhere when user clicks the confirmation link. And then you could check whether EmailConfirmed equals true when validating.

    public class NoDirectAccessAttribute : ActionFilterAttribute
    {
        public override void OnActionExecuting(ActionExecutingContext filterContext)
        {
            var canAcess= false;

            // check the `EmailConfirmed` state

            // ... check other requirements

            if(!canAcess){
                filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Home", action = "Index", area = "" })); 
            }
        }
    }
Sign up to request clarification or add additional context in comments.

Comments

2

In general it is not a good idea to depend on the "referer" properties on requests. Browser implementations could be different & as a result you may receive inconsistent values.

If a URL is "public", you cannot prevent a user from typing that on a browser. What you could do however, is add validation to the "Get" request - for example, requiring some additional information on the URL - in your case, say the User id and a validation "code". This is how asp.net identity generates a URL for email confirmation. It includes the user id & a confirmation code. Without these values as part of the URL, it is possible to invalidate the request and redirect to another page (rather than the Email confirmed page).

For example, in Razor pages, the following is an example of how you could validate the request. (The point is, it requires additional input from the user rather than the plain URL..) (You could do something similar in MVC as well.)

       public async Task<IActionResult> OnGetAsync(string userId, string code)
        {
            if (userId == null || code == null)
            {
                return RedirectToPage("/Index");
            }

            var user = await _userManager.FindByIdAsync(userId);
            if (user == null)
            {
                throw new ApplicationException($"Unable to load user with ID '{userId}'.");
            }

            var result = await _userManager.ConfirmEmailAsync(user, code);
            if (!result.Succeeded)
            {
                throw new ApplicationException($"Error confirming email for user with ID '{userId}':");
            }

            return Page();
        }

The URL for the above code to validate would be something similar to this:

http://[baseurl]/Account/ConfirmEmail?userId=ca5416dd-c93a-49a5-8fac-a9986dc91ed7&code=CfDJ8DWvHoaRg6JAv0rZ75jyEVx

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.