Add token parameter to all urls inside an asp.net mvc 2 site - asp.net

I've integrated some pages written in ASP.NET MVC 2, into a classic webform app.
Everything works well except the authentication system.
The authentication system is using some token added to the url like :
/Account/Profil/Details.aspx?AUTHID=2ddc098a-cf0b-fd81-afb7-d41f35010b9f
When i reach my asp.net mvc pages (all these pages must be secured), they must include that AUTHID parameter.
I'm using the core Webform control to secure the pages, and this control check for the AUTHID token in the url. So basicly my route must include the
?AUTHID=2ddc098a-cf0b-fd81-afb7-d41f35010b9f
What the best and clever way to do this ?
I don't want to pass the AUTHID parameter manually in all controller actions.
Thanks for your help.

You can solve your problem by extending the ASP.NET routing mechanism. Just create a custom route and override the GetVirtualPath function.
public class TokenizedRoute : Route
{
public TokenizedRoute(string url, IRouteHandler routeHandler) : base(url, routeHandler)
{
}
public override VirtualPathData GetVirtualPath(RequestContext requestContext, RouteValueDictionary values)
{
string tokenValue = "your token value";
values.Add("AUTHID", tokenValue);
return base.GetVirtualPath(requestContext, values);
}
}
See my blog post for more details.

You could use a jQuery solution to append a token to the query string of all links:
$("a").each(function (index, link)
{
$(link).attr("href", $(link).attr("href") + "?AUTHID=" + token);
});
But I agree with dknaack, I would say you should reconsider your authentication logic if at all possible.

You can save the AuthId in the Session object and create a custom Authorize Attribute.
Attribute
public class CustomAuthorize : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
// your custom logic depending on Session["AuthId"]
return httpContext.Session["AuthId"] != null;
}
}
Controller
public class MyController : Controller
{
[CustomAuthorize]
public ActionResult MyActionMethod()
{
return View();
}
}
hope this helps

Related

Correct way to validate query string parameters to authorize a ASP.NET CORE request

Shopify allows to embed pages into the admin site, to do that I can create a page using ASP.NET MVC and get the page shown in the admin panel of Shopify.
To validate if the page request is valid and was request by Shopify there are some parameters sent in the query string that the page has to validate before processing the request and render the page.
Shopify sends a hmac parameter so I can calculate the same parameter and validate if both are equal.
I was using Asp.Net Mvc 5 and used the AuthorizeAttribute class but now I am using Asp.Net Core and it seems authorization filters have changed.
I have read some articles about how is the new authorization system in Asp.Net Core but I can't determine what is the best way to do it.
So in the end I need:
Crete a custom attribute so I can add it to my controllers. When Shopify calls my pages I need to verify the query string parameters before the controller action starts to process the request, in the case the request is not valid the controller action is not called but if the request is valid it authorizes and lets the controller action to execute and render the page.
My current filter in Asp.Net MVC 5 is something like this:
namespace MyShopifyApp.Filters
{
public class EmbeddedAppAuthAttribute : AuthorizeAttribute
{
protected override bool AuthorizeCore(HttpContextBase httpContext)
{
//Validates if the nonce/state from the query string is correct
var stateParameter = httpContext.Request.QueryString["state"];
var nonce = ShopifyHelper.AuthorizationNonceManager.GetNonce(ProjectSettings.ShopifyShopUrl);
if (!string.IsNullOrEmpty(stateParameter))
{
if (string.IsNullOrEmpty(nonce) || stateParameter != nonce)
{
return false;
}
}
//Validates if the shop parameter from the query string is valid
var shopParameter = httpContext.Request.QueryString["shop"];
if (!ProjectSettings.IsValidShop(shopParameter))
return false;
//Calculates a HMAC signature and validates if the request is really from Shopify
if (!ShopifyAuthorizationService.IsAuthenticRequest(httpContext.Request.QueryString, ProjectSettings.ShopifyAdminAppApiSecret))
return false;
//Everything is correct so allow the request to continue
return true;
}
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
}
}
}
This is an example controller:
namespace MyShopifyApp.Controllers
{
[EmbeddedAppAuth]
public class MyController : Controller
{
public async Task<ActionResult> Index(string hmac, string shop, string signature, string timeStamp, string protocol)
{
//Do something here only if the request is authentic and sent by Shopify
}
}
}

ASP.NET MVC custom routing with default route

I have a bit of problem
I have an MVC site that is working just fine.
I wanted to have SEO friendly urls so I created custom routing that looks like this
{section}/{group}/{manufacturer}
since it has the same number of fields as the default route
{controller}/{action}/{id}
i created in loop in RouteConfig.cs that goes to database and loops Sections table and creates custom routes like
FOOD/{group}/{manufacturer}
SPORT/{group}/{manufacturer}
CARS/{group}/{manufacturer}
and point it to the Sections controller.
It works just fine. The problem I have is that now i need to rename FOOD to FRESH-FOOD and i want to keep old links to FOOD working.
What would you recommend ? How can I solve this ?
Is there a better way to do route Section instead of custom routes ?
Sounds like this might be a good case for a custom route handler. In the ProcessRequest method below you can check for controller name of "FOOD" and redirect to "FRESH-FOOD" like this:
public class CustomRouteHandler : IRouteHandler
{
public IHttpHandler GetHttpHandler(RequestContext requestContext)
{
return new CustomHttpHandler();
}
}
public class CustomHttpHandler : IHttpHandler
{
public bool IsReusable
{
get
{
return true;
}
}
public void ProcessRequest(HttpContext context)
{
var routeValues = context.Request.RequestContext.RouteData.Values;
var controllerName = context.Request.RequestContext.RouteData.GetRequiredString("controller");
if (controllerName.ToLower() == "food") controllerName = "FRESH-FOOD";
var controller = ControllerBuilder.Current.GetControllerFactory().
CreateController(context.Request.RequestContext, controllerName);
if (controller != null)
{
controller.Execute(context.Request.RequestContext);
}
}
}
When you create your custom routes use the Route constructor overload that takes an IRouteHandler

ASP.NET allow anonymous access to OData $metadata when site has global AuthorizeAttribute

I have an ASP.NET OData site that has the following in the WebApiConfig file:
config.Filters.Add(new AuthorizeAttribute())
This forces all callers to authenticate before calling any of the controllers.
Unfortunately, this also forces user authentication to access the "$metadata" url.
I need to globally force authentication for all controller access while also allowing anonymous access the the "$metadata" url.
I realize this question has already been answered, but I have a couple concerns with the accepted answer:
Assumes the metadata endpoint will not change
Requires updating the code if an endpoint is added/moved
Does not handle the root endpoint (without /$meatdata)
I agree with creating your own AuthorizeAttribute, but I would implement the method a little differently.
protected override bool IsAuthorized(HttpActionContext actionContext)
{
if (actionContext.ControllerContext.Controller is System.Web.OData.MetadataController)
return true;
return base.IsAuthorized(actionContext);
}
My solution simply checks to see if the controller being accessed is OData's MetadataController. If it is, allow anyone access, otherwise, go through the normal authorization checks.
Create a custom filter that derives from AuthorizeAttribute and override the IsAuthorized method as follows:
public class CustomAuthorizationFilter : AuthorizeAttribute
{
protected override bool IsAuthorized(HttpActionContext actionContext)
{
if (actionContext.Request.RequestUri.AbsolutePath == "/$metadata" ||
actionContext.Request.RequestUri.AbsolutePath == "/%24metadata")
{
return true;
}
return base.IsAuthorized(actionContext);
}
}
Register the filter:
config.Filters.Add(new CustomAuthorizationFilter());
I wanted to add one more option. If you replace the default Web API dependency resolver (HttpConfiguration.DependencyResolver = YourIDependencyResolver) you can intercept the request for the metadata controller (ODataMetadataController or MetadataController, depending on the version of the OData library) and replace it with your own implementation, like below:
[AllowAnonymous, OverrideAuthorization]
public class AnonymousODataMetadataController : ODataMetadataController
{
protected override void Initialize(HttpControllerContext controllerContext)
{
// You must replace the controller descriptor because it appears
// that the AuthorizeAttribute is pulled from the
// controllerContext.ControllerDescriptor.ControllerType (which
// is the original type) instead of from controlContext.Controller
// (which is the type we injected).
controllerContext.ControllerDescriptor = new HttpControllerDescriptor
{
Configuration = controllerContext.Configuration,
ControllerName = GetType().Name,
ControllerType = GetType()
};
base.Initialize(controllerContext);
}
}
See Dependency Injection in ASP.NET Web API 2 for info about the Web API dependency injection system.

ASP.NET MVC 2.0 JsonRequestBehavior Global Setting

ASP.NET MVC 2.0 will now, by default, throw an exception when an action attempts to return JSON in response to a GET request. I know this can be overridden on a method by method basis by using JsonRequestBehavior.AllowGet, but is it possible to set on a controller or higher basis (possibly the web.config)?
Update: Per Levi's comment, this is what I ended up using-
protected override JsonResult Json(object data, string contentType, System.Text.Encoding contentEncoding)
{
return Json(data, contentType, JsonRequestBehavior.AllowGet);
}
This, like other MVC-specific settings, is not settable via Web.config. But you have two options:
Override the Controller.Json(object, string, Encoding) overload to call Json(object, string, Encoding, JsonRequestBehavior), passing JsonRequestBehavior.AllowGet as the last argument. If you want this to apply to all controllers, then do this inside an abstract base controller class, then have all your controllers subclass that abstract class.
Make an extension method MyJson(this Controller, ...) which creates a JsonResult and sets the appropriate properties, then call it from your controller via this.MyJson(...).
There's another option. Use Action Filters.
Create a new ActionFilterAttribute, apply it to your controller or a specific action (depending on your needs). This should suffice:
public class JsonRequestBehaviorAttribute : ActionFilterAttribute
{
private JsonRequestBehavior Behavior { get; set; }
public JsonRequestBehaviorAttribute()
{
Behavior = JsonRequestBehavior.AllowGet;
}
public override void OnResultExecuting(ResultExecutingContext filterContext)
{
var result = filterContext.Result as JsonResult;
if (result != null)
{
result.JsonRequestBehavior = Behavior;
}
}
}
Then apply it like this:
[JsonRequestBehavior]
public class Upload2Controller : Controller
MVC 2 block Json for GET requests for security reasons. If you want to override that behavior, check out the overload for Json that accepts a JsonRequestBehavior parameter.
public ActionResult Index()
{
return Json(data, JsonRequestBehavior.AllowGet)
}
I also got this error when I first use MVC 2.0 using my old code in MVC 1.0. I use fiddler to identify the cause of the error. See the steps on how to troubleshoot it using Fidder -
http://www.rodcerrada.com/post/2011/07/11/jQuery-getJSON()-does-not-tirgger-the-callback-in-ASPNET-MVC-2.aspx
Is this is the security issue MVC2 was trying to address?
http://haacked.com/archive/2009/06/25/json-hijacking.aspx
If so, it seems like the vulnerability is only an issue if you are trying to do a json call to an outside website. If your MVC2 app is only making json calls to your own website (to fill jqgrids for example), shouldn't you be able to safely override the Json call in your base controller to always allow get?
Just change JSON code from :
$.getJson("methodname/" + ID, null, function (data, textStatus)
to:
$.post("methodname/" + ID, null, function (data, textStatus)

Disable Session state per-request in ASP.Net MVC

I am creating an ActionResult in ASP.Net MVC to serve images. With Session state enabled, IIS will only handle one request at a time from the same user. (This is true not just in MVC.)
Therefore, on a page with multiple images calling back to this Action, only one image request can be handled at a time. It's synchronous.
I'd like this image Action to be asynchronous -- I'd like multiple image requests to each execute without needing the previous one to complete. (If the images were just static files, IIS would serve them up this way.)
So, I'd like to disable Session just for calls to that Action, or to specify that certain requests do not have Session state. Anyone know how this is done in MVC? Thanks!
If anyone is in the situation I was in, where your image controller actually needs read only access to the session, you can put the SessionState attribute on your controller
[SessionState(SessionStateBehavior.ReadOnly)]
See http://msdn.microsoft.com/en-us/library/system.web.mvc.sessionstateattribute.aspx for more info.
Thanks to https://stackoverflow.com/a/4235006/372926
Rather than implementing an action filter for this, why don't you implement a RouteHandler?
Here's the deal - IRouteHandler has one method - GetHttpHandler. When you make an ASP.Net MVC request to a controller, by default the routing engine handles the request by creating a new instance of MvcRouteHandler, which returns an MvcHandler. MvcHandler is an implementation of IHttpHandler which is marked with the (surprise!) IRequiresSessionState interface. This is why a normal request uses Session.
If you follow my blog post on how to implement a custom RouteHandler (instead of using MvcRouteHandler) for serving up images - you can skip returning a session-tagged IHttpHandler.
This should free IIS from imposing synchronicity on you. It would also likely be more performant because it's skipping all the layers of the MVC code dealing with filters.
I also came across the same problem and after doing R&D this link worked for me
Reference:
https://techatfingers.wordpress.com/2016/06/14/session-state-on-action/
Create custom Attribute
Override the “GetControllerSessionBehavior” method present in class DefaultControllerFactory.
Register it in global.aspx
1> Create custom Attribute
public sealed class ActionSessionStateAttribute : Attribute
{
public SessionStateBehavior SessionBehavior { get; private set; }
public ActionSessionStateAttribute(SessionStateBehavior sessionBehavior)
{
SessionBehavior = sessioBehavior;
}
}
2. Override
public class SessionControllerFactory : DefaultControllerFactory
{
protected override SessionStateBehavior GetControllerSessionBehavior(RequestContext requestContext, Type controllerType)
{
if (controllerType == null)
return SessionStateBehavior.Default;
var actionName = requestContext.RouteData.Values["action"].ToString();
Type typeOfRequest=requestContext.HttpContext.Request.RequestType.ToLower() =="get"?typeof(HttpGetAttribute):typeof(HttpPostAttribute);
// [Line1]
var cntMethods = controllerType.GetMethods()
.Where(m =>
m.Name == actionName &&
( ( typeOfRequest == typeof(HttpPostAttribute) &&
m.CustomAttributes.Where(a => a.AttributeType == typeOfRequest).Count()>0
)
||
( typeOfRequest == typeof(HttpGetAttribute) &&
m.CustomAttributes.Where(a => a.AttributeType == typeof(HttpPostAttribute)).Count() == 0
)
)
);
MethodInfo actionMethodInfo = actionMethodInfo = cntMethods != null && cntMethods.Count() == 1 ? cntMethods.ElementAt(0):null;
if (actionMethodInfo != null)
{
var sessionStateAttr = actionMethodInfo.GetCustomAttributes(typeof(ActionSessionStateAttribute), false)
.OfType<ActionSessionStateAttribute>()
.FirstOrDefault();
if (sessionStateAttr != null)
{
return sessionStateAttr.Behavior;
}
}
return base.GetControllerSessionBehavior(requestContext, controllerType);
}
3. Register class in Global.asax
public class MvcApplication : System.Web.HttpApplication
{
protected void Application_Start()
{
// --- other code ---
ControllerBuilder.Current.SetControllerFactory(typeof(SessionControllerFactory));
}
}
Try serving the images from another domain. So something like images.mysite.com.
This will provide you two benefits: One, sessions are tracked by a cookie, so images.mysite.com won't have the cookie. Two, it will give you an additional two concurrent requests to retrieve images.
Have you considered setting up a HttpHandler to serve up your images?
SessionState attribute is quite helpful if u use mvc3. How to achieve this with mvc2 needs a little more coding.
Idea is to tell the asp.net that specific request wont use session object.
So, Create a custom route handler for specific requests
public class CustomRouteHandler : IRouteHandler
{
public System.Web.IHttpHandler GetHttpHandler(RequestContext requestContext)
{
requestContext.HttpContext.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.ReadOnly);
return new MvcHandler(requestContext);
}
}
SessionStateBehavior enum has 4 members, you should use "disabled" or "readonly" modes to get async behavior.
After creating this custom route handler, be sure that your specific requests goes through this handler. This can be done via defining new routes at Global.asax
routes.Add("Default", new Route(
"{controller}/{action}",
new RouteValueDictionary(new { controller = "Home", action = "Index"}),
new CustomRouteHandler()
));
Adding this route makes all your requests to be handled by your custom route handler class. You can make it specific by defining different routes.
Change DefaultCOntrollerFactory to custom ControllerFactory class. Default Controller.TempDataProvider use SessionStateTempDataProvider. you can change it.
1.Set web.config/system.web/sessionState:mode="Off".
2.create DictionaryTempDataProvider class.
public class DictionaryTempDataProvider : ITempDataProvider
{
public IDictionary<string, object> LoadTempData(ControllerContext controllerContext)
{
return new Dictionary<string, object>();
}
public void SaveTempData(ControllerContext controllerContext, IDictionary<string, object> values)
{
}
}
3.Create DictionaryTempDataControllerFactory
public class DictionaryTempDataControllerFactory : DefaultControllerFactory
{
public override IController CreateController(System.Web.Routing.RequestContext requestContext, string controllerName)
{
var controller = base.CreateController(requestContext, controllerName) as Controller;
if (controller!=null)
controller.TempDataProvider = new DictionaryTempDataProvider();
return controller;
}
}
4.In global.asax.cs Apprication_Start event set DictionaryTempDataControllerFactory.
protected void Application_Start()
{
RegisterRoutes(RouteTable.Routes);
ControllerBuilder.Current.SetControllerFactory(
new DictionaryTempDataControllerFactory()
);
}
On our server, IIS doesn't even know about sessions - it's the ASP.NET stack that handles one request per session at a time. Static files, like images, are never affected.
Is it possible that your ASP.NET app is serving the files instead of IIS?
Create new Controller
Decorate controler with [SessionState(SessionStateBehavior.Disabled)]
Refactor code you want seesion stated disabled for to that controller
I would to share my solution for disable ASP.NET Session for an specific request (in my case, a WCF Service) using an HttpModule:
public class AspNetSessionFilterModule : IHttpModule
{
public void Init(HttpApplication context)
{
context.PostMapRequestHandler += OnPostMapRequestHandler;
}
private void OnPostMapRequestHandler(object sender, EventArgs e)
{
var context = (sender as HttpApplication).Context;
DisableSessionForSomeRequests(context);
}
private void DisableSessionForSomeRequests(HttpContext context)
{
if ("~/Services/MyService.svc".Equals(context.Request.AppRelativeCurrentExecutionFilePath, StringComparison.InvariantCultureIgnoreCase))
{
context.SetSessionStateBehavior(System.Web.SessionState.SessionStateBehavior.Disabled);
}
}
public void Dispose()
{ }
}

Resources