Authorize attribute is letting ActionResult code execute - ajax

I have a custom attribute that I am using for Ajax requests to prevent the default FormsAuthentication workflow from happening (since it doesn't make sense for an Ajax request).
This is the custom authorize attribute:
public class AjaxAuthorize : AuthorizeAttribute {
protected override void HandleUnauthorizedRequest(AuthorizationContext filterContext) {
UrlHelper urlHelper;
if (filterContext.HttpContext.Request.IsAjaxRequest()) {
urlHelper = new UrlHelper(filterContext.RequestContext);
filterContext.HttpContext.Response.StatusCode = 401;
//Don't let IIS7 be mean.
filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
//Return JSON which tells the client where the login page exists if the user wants to authenticate.
filterContext.HttpContext.Response.Write(new JavaScriptSerializer().Serialize(
new {
LoginUrl = string.Format("{0}?ReturnURL={1}", FormsAuthentication.LoginUrl, urlHelper.Encode(filterContext.HttpContext.Request.Url.PathAndQuery))
}
));
filterContext.HttpContext.Response.End();
} else {
base.HandleUnauthorizedRequest(filterContext);
}
}
}
I have my ActionResult decorated with the custom authorize attribute like so:
[HttpPut]
[ValidateInput(false)]
[AjaxAuthorize]
public ActionResult Comment(string id, string comment) {
//stuff happens here.
}
An exception is thrown inside of my action result because I try to access information about a user that would only exist if you are logged in. But, I don't understand why this code is even being executed because I have my authorization attribute protecting the action.

protected virtual void HandleUnauthorizedRequest(AuthorizationContext filterContext)
{
// Returns HTTP 401 - see comment in HttpUnauthorizedResult.cs.
filterContext.Result = new HttpUnauthorizedResult();
}
This is default implementation of HandleUnauthorizedRequest. Probably you need to assign your custom ActionResult that contains JSON you need and valid status code. I'm not sure direct processing request object from HTTP context would work.

I ended up returning an HTTP 418 (I have been wanting to use this for awhile) so that the Forms Authentication module didn't intercept the request. Then in my script I look for that status code and handle it as an unauthorized request and do the proper redirect.

Try calling base.HandleUnauthorizedRequest(filterContext); inside the IF block. Or just remove the ELSE block and always call base.HandleUnauthorizedRequest(filterContext);.

Related

HttpContext.Session.Abandon() doesn't work in MVC core. Session.clear doesn't log my user out

I get an error that says "ISession does not contain a definition for 'Abandon' and no accessible extension method 'Abandon' accepting a first argument of type 'ISession' could be found".
I have tried using session.clear but even after logging out if I open the website the user is logged in.
This is the error I get
This is how I have implemented Session in my ASP .NET CORE project:
Create a SessionTimeout filter:
public class SessionTimeout : ActionFilterAttribute
{
public override void OnActionExecuting(ActionExecutingContext context)
{
if (context.HttpContext.Session == null ||!context.HttpContext.Session.TryGetValue("UserID", out byte[] val))
{
context.Result =
new RedirectToRouteResult(new RouteValueDictionary(new
{
controller = "Pages",
action = "SessionTimeout"
}));
}
base.OnActionExecuting(context);
}
}
Register this filter in your Startup.cs:
In your ConfigureServices method:
services.AddSession(options =>
{
options.IdleTimeout = TimeSpan.FromMinutes(10);
});
In your Configure add:
app.UseSession();
And finally decorate your class/method with your filter like:
[SessionTimeout]
public class DashboardController : Controller
To destroy your session based on Logout event from your View:
public IActionResult Logout()
{
HttpContext.Session.Clear();
return RedirectToAction("Login", new { controller = "Pages" });
}
It seems my session is being stored in cookies and not getting cleared/deleted when used session.clear()
so I've used this and it seems to work like a charm.
foreach (var cookie in Request.Cookies.Keys)
{
if (cookie == ".AspNetCore.Session")
Response.Cookies.Delete(cookie);
}
HttpContext.Session.Clear() wasn't working for me on my live site in the Controller for my Account/Logout page.
I found out that setting a href of /Account/Logout/ was the problem. I changed my links to /Account/Logout.
If you're having Session problems in .NET Core 3.1 then try this. It may also account for why I couldn't get Cookie Authentication to work - I gave up in the end and switched to using Sessions.

Display message to user on expired session when using wicket-auth-roles

Hi I have been unable to solve the following problem in Wicket 6.*:
In our webapp we are using wicket-auth-roles to manage authentication/authorization. When session expires, user should be redirected to a page set by getApplicationSettings().setPageExpiredErrorPage(SomePage.class) on his next action. However, if the user tries to access a page which doesn't allow guests, he is redirected to a login page skipping the PageExpiredPage altogether.
My question is - how can I display "Session has expired." message to the user?
Among other things, I have tried session.info("message") during onInvalidate phase of session's lifecycle, however the feedback message is then rendered on the first page after login (not on the login page).
Thank you for your anwsers.
You could use a RequestCycleListener to record when a PageExpiredException is thrown.
public class ExceptionMapperListener extends AbstractRequestCycleListener {
#Override
public IRequestHandler onException(RequestCycle cycle, Exception ex) {
if (ex instanceof PageExpiredException) {
// Record in session or request cycle
// OR
// Create a RenderPageRequestHandler yourself and add a page parameter
// See DefaultExceptionMapper#internalMap(Exception)
}
return null;
}
}
// In Application#init():
getRequestCycleListeners().add(new ExceptionMapperListener());
ORINAL ANSWER
(kept because it could still help...)
I haven't tried it myself since I don't use wicket-auth-roles, but try overriding the method AuthenticatedWebApplication#restartResponseAtSignInPage() with something like this:
if (isSessionExpired()) {
PageParameters params = new PageParameters();
params.add("showSessionExpired", true);
throw new RestartResponseAtInterceptPageException(getSignInPageClass(), params);
} else {
throw new RestartResponseAtInterceptPageException(getSignInPageClass());
}
And then in the SignInPageClass, display the desired message if the showSessionExpired page parameter is present.
I'm not sure how you implement isSessionExpired(), but you seem to have that part already covered.
OR
Depending on how you implemented isSessionExpired(), maybe you could do the following in your SignInPageClass:
if (sessionExpired()) {
session.info("message")
}
After bernie put me on the right path, I eventually figured out a solution to the problem:
First it is required to override RequestCycleListener:
public class SessionExpiredListener extends AbstractRequestCycleListener {
public void onRequestHandlerResolved(RequestCycle cycle, IRequestHandler handler) {
if (handler instanceof IPageRequestHandler) {
IPageRequestHandler pageHandler = (IPageRequestHandler) handler;
HttpServletRequest request = (HttpServletRequest) cycle.getRequest().getContainerRequest();
//check whether the requested session has expired
boolean expired = request.getRequestedSessionId() != null && !request.isRequestedSessionIdValid();
//check whether the requested page can be instantiated with the current session
boolean authorized = Session.get().getAuthorizationStrategy().isInstantiationAuthorized(pageHandler.getPageClass());
if (expired && !authorized) {
throw new PageExpiredException("Session has expired!");
}
}
super.onRequestHandlerResolved(cycle, handler);
}
}
Check for authorized prevents the session-expired message from displaying on log-out or when accessing unprotected pages.
Finally, you must register your listener and PageRequestHandlerTracker in your WebApplication:
getRequestCycleListeners().add(new SessionExpiredListener());
getRequestCycleListeners().add(new PageRequestHandlerTracker());

display message on LogOn page when session time out in asp.net mvc3

i want to display session time out message on my LogOn page.
i have override asp.net mvc Authorize attribute to naviagate the user to logOn page on session timeOut but my problem is that i am not able to set ViewData or ViewBag or Session["Message"]
kind of thing inside that override method.
i would request you all to please help me ...
how to set ViewData inside that Override method.
below is my CustomAuthorizeAttribute class
public class CustomAuthorizeAttribute : AuthorizeAttribute
{
public override void OnAuthorization(AuthorizationContext filterContext)
{
if (filterContext == null)
{
throw new ArgumentNullException("filterContext");
}
if (!filterContext.HttpContext.User.Identity.IsAuthenticated || SessionCache.User_ID == 0)
{
// User not logged in, redirect to login page
filterContext.Result = new HttpUnauthorizedResult();
return;
}
}
}
please suggest.
thanks,
You want to look into using Session_End event in your Global.asax.
It's important to realize that Session_End will only be called on InProc sessions.

How can I show Authenticated but UNAUTHORIZED users an unauthorized page MVC 3?

I have an application where some users belong to a Role, but may not actually have access to certain data within a URL. For instance the following url is open to all users
/Library/GetFile/1
However, some users may not have access to file1, but I can't use the Authorize attribute to detect that. I want instead to redirect those users to an unauthorized or accessdenied page. I'm using Forms Authentication and my config is set up like this
<authentication mode="Forms">
<forms loginUrl="~/Home/Index" timeout="2880" />
</authentication>
my custom errors block is like this
<customErrors mode="On" defaultRedirect="Error" redirectMode="ResponseRewrite" >
<error statusCode="401" redirect="Unauthorized"/>
</customErrors>
I am attempting to return the HttpUnauthorizedResult if the user does not have access, but I just get redirected to the login page, which isn't valid here because the User is Authenticated already.
It appears that the HttpUnauthorizedResult is setting the HTTP Response Code to 401 which Forms Authentication is hijacking and sending the user to the Login page.
Throwing the UnauthorizedAccessException doesn't seem to work either always redirecting the user to an IIS Error page even though I've updated my RegisterGlobalFilters to
filters.Add(new HandleErrorAttribute
{
ExceptionType = typeof(UnauthorizedAccessException),
View = "Unauthorized",
Order = 3
});
If I change UnauthorizedAccessException to a custom Exception the redirect works and for now that's what I've done.
Your solution is similar to mine except that I did this:
Create a custom exception, UnauthorizedDataAccessException.
Create a custom exception filter (so that it could log the invalid access attempt).
Register my custom exception attribute as a global filter in App_start.
Create a marker interface, ISecureOwner and added it to my entity.
Add a secure 'Load' extension method to my repository, which throws the exception if the current user is not the owner of the entity that was loaded. For this to work, entity has to implement ISecureOwner that returns the id of the user that saved the entity.
Note that this just shows a pattern: the details of how you implement GetSecureUser and what you use to retrieve data will vary. However, although this pattern is okay for a small app, it is a bit of hack, since that kind of security should be implemented deep down at the data level, using ownership groups in the database, which is another question :)
public class UnauthorizedDataAccessException : Exception
{
// constructors
}
public class UnauthorizedDataAccessAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.Exception.GetType() == Typeof(UnauthorizedDataAccessException))
{
// log error
filterContext.ExceptionHandled = true;
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary(new { controller = "Error", action = "UnauthorizedDataAccess" }));
}
else
{
base.OnException(filterContext);
}
}
// marker interface for entity and extension method
public interface ISecureOwner
{
Guid OwnerId { get; }
}
// extension method
public static T SecureFindOne<T>(this IRepository repository, Guid id) where T : class, ISecureOwner, new()
{
var user = GetSecureUser();
T entity = repository.FindOne<T>(id);
if (entity.OwnerId != user.GuidDatabaseId)
{
throw new UnauthorizedDataAccessException(string.Format("User id '{0}' attempted to access entity type {1}, id {2} but was not the owner. The real owner id is {3}.", user.GuidDatabaseId, typeof(T).Name, id, entity.OwnerId));
}
return entity;
}
// Register in global.asax
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
var filter = new UnauthorizedDataAccessAttribute { ExceptionType = typeof(UnauthorizedDataAccessException) };
filters.Add(filter);
filters.Add(new HandleErrorAttribute());
}
// Usage:
var ownedThing = myRepository.SecureFindOne<myEntity>(id))
You can restrict access to certain roles. If an unauthorized role tries to access a resource you can redirect them to a specific url.
Look at this other SO question: attribute-for-net-mvc-controller-action-method, there are good answers there.
You can check in your code if a user belongs to a role:
User.IsInRole("RoleToTest");
you can also apply attributes to your controllers/action methods. Anyhow it is all explained in the link I specified above.
* EDIT *
You could override OnException in your base Controller. Implement a custom exception, e.g., AccessNotAuthorizedAccessException.
In OnExcepton, if you detect your custom exception, just redirect to a friendly url that shows the 'Not authorized...' message.

Why isn't my overriden OnAuthorization returning the filterContext.Result that I set?

Here is the code for my base controller, the idea is that if the Authorization string is not in the HTTP Headers we kick them out. I swear it was working properly and now suddenly it does not work. Strangely when I debug it is actually stepping INTO the if statement so it is indeed true that the HTTP Header I am requesting is a NULL OR EMPTY string, HOWEVER, it is not exiting early and returning 403 Access Denied anymore... it was working fine and suddenly it is just ignoring the entire thing and eventually crashing later on in the app when I try to parse the Authorization String that WAS NOT ACTUALLY FOUND.
public class AuthController : Controller
{
protected int AccountID;
protected override void OnAuthorization(AuthorizationContext filterContext)
{
//if no authorization string is provided, access denied
if (string.IsNullOrEmpty(filterContext.HttpContext.Request.Headers["Authorization"]))
{
filterContext.Result = Content("Access Denied", "text/plain");
filterContext.HttpContext.Response.StatusCode = 403; //forbidden
base.OnAuthorization(filterContext);
}
//otherwise grab the authorization string and validate it
string authString = filterContext.HttpContext.Request.Headers["Authorization"];
string urlPath = string.IsNullOrEmpty(filterContext.HttpContext.Request.Path) ? "" : filterContext.HttpContext.Request.Path;
int getAccountID = 0;
//if authorization fails...
if (!AuthCore.Authorize(authString, urlPath, ref getAccountID))
{
filterContext.Result = Content("Access Denied", "text/plain");
filterContext.HttpContext.Response.StatusCode = 403; //forbidden
base.OnAuthorization(filterContext);
}
//AccountID will never be zero at this point
AccountID = getAccountID;
//carry on with Controller Action, request is valid and AccountID is known
base.OnAuthorization(filterContext);
}
UPDATE: Just tried filterContext.Result = new HttpUnauthorizedResult(); instead, same results. Controller action continues and throws error when trying to parse the header string that was not found.
UPDATE 2: Added "return;" after each of the base.OnAuthorization() calls besides the last one, now when it fails I get a 302 moved from MVC followed by a 404, which turns out is the app trying to redirect to a default login page URL that does not actually exist... could this be good enough? Maybe but I'd rather block it straight out rather than letting some wonky redirect happen as the way of blocking them, doesn't feel secure to me.
AH HA!
I was calling the base.OnAuthorization() too many times, apparently it's not actually a permanent goodbye from the thread... not sure why I thought it was now that I think about it... here is the working code:
protected override void OnAuthorization(AuthorizationContext filterContext)
{
int getAccountID = 0;
//if no authorization string is provided, access denied
if (string.IsNullOrEmpty(filterContext.HttpContext.Request.Headers["Authorization"]))
{
filterContext.Result = Content("Access Denied", "text/plain");
filterContext.HttpContext.Response.StatusCode = 403; //forbidden
}
else
{
//otherwise grab the authorization string and validate it
string authString = filterContext.HttpContext.Request.Headers["Authorization"];
string urlPath = string.IsNullOrEmpty(filterContext.HttpContext.Request.Path) ? "" : filterContext.HttpContext.Request.Path;
//if authorization fails...
if (!AuthCore.Authorize(authString, urlPath, ref getAccountID))
{
filterContext.Result = Content("Access Denied", "text/plain");
filterContext.HttpContext.Response.StatusCode = 403; //forbidden
}
}
//AccountID will never be zero at this point
AccountID = getAccountID;
//carry on with Controller Action, request is valid and AccountID is known
base.OnAuthorization(filterContext);
}
I think you should check out this post:
Securing your ASP.NET MVC 3 Application

Resources