Reputation: 11
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()
Upvotes: 1
Views: 5420
Reputation: 559
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
Upvotes: 2
Reputation: 25360
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();
}
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 = "" }));
}
}
}
Upvotes: 3