Rectangle 27 280

When it was first developed, System.Web.Mvc.AuthorizeAttribute was doing the right thing - older revisions of the HTTP specification used status code 401 for both "unauthorized" and "unauthenticated".

If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials.

In fact, you can see the confusion right there - it uses the word "authorization" when it means "authentication". In everyday practice, however, it makes more sense to return a 403 Forbidden when the user is authenticated but not authorized. It's unlikely the user would have a second set of credentials that would give them access - bad user experience all around.

Consider most operating systems - when you attempt to read a file you don't have permission to access, you aren't shown a login screen!

Thankfully, the HTTP specifications were updated (June 2014) to remove the ambiguity.

From "Hyper Text Transport Protocol (HTTP/1.1): Authentication" (RFC 7235):

The 401 (Unauthorized) status code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.

From "Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content" (RFC 7231):

The 403 (Forbidden) status code indicates that the server understood the request but refuses to authorize it.

Interestingly enough, at the time ASP.NET MVC 1 was released the behavior of AuthorizeAttribute was correct. Now, the behavior is incorrect - the HTTP/1.1 specification was fixed.

Rather than attempt to change ASP.NET's login page redirects, it's easier just to fix the problem at the source. You can create a new attribute with the same name (AuthorizeAttribute) in your website's default namespace (this is very important) then the compiler will automatically pick it up instead of MVC's standard one. Of course, you could always give the attribute a new name if you'd rather take that approach.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAuthenticated)
        {
            filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}
filterContext.HttpContext.User.Identity.IsAuthenticated
filterContext.HttpContext.Request.IsAuthenticated

Great idea - I've updated the answer.

> You can create a new attribute with the same name (AuthorizeAttribute) in your website's default namespace then the compiler will automatically pick it up instead of MVC's standard one. This results in an error: The type or namespace 'Authorize' could not be found ( are you missing a directive or an assembly reference?) Both using System.Web.Mvc; and the namespace for my custom AuthorizeAttribute class are referenced in the controller. To solve this I had to use [MyNamepace.Authorize]

@DePeter the spec never says anything about a redirect so why is a redirect a better solution? This alone kills ajax requests without a hack in place to solve it.

asp.net mvc - Why does AuthorizeAttribute redirect to the login page f...

asp.net-mvc authentication authorization
Rectangle 27 280

When it was first developed, System.Web.Mvc.AuthorizeAttribute was doing the right thing - older revisions of the HTTP specification used status code 401 for both "unauthorized" and "unauthenticated".

If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials.

In fact, you can see the confusion right there - it uses the word "authorization" when it means "authentication". In everyday practice, however, it makes more sense to return a 403 Forbidden when the user is authenticated but not authorized. It's unlikely the user would have a second set of credentials that would give them access - bad user experience all around.

Consider most operating systems - when you attempt to read a file you don't have permission to access, you aren't shown a login screen!

Thankfully, the HTTP specifications were updated (June 2014) to remove the ambiguity.

From "Hyper Text Transport Protocol (HTTP/1.1): Authentication" (RFC 7235):

The 401 (Unauthorized) status code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.

From "Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content" (RFC 7231):

The 403 (Forbidden) status code indicates that the server understood the request but refuses to authorize it.

Interestingly enough, at the time ASP.NET MVC 1 was released the behavior of AuthorizeAttribute was correct. Now, the behavior is incorrect - the HTTP/1.1 specification was fixed.

Rather than attempt to change ASP.NET's login page redirects, it's easier just to fix the problem at the source. You can create a new attribute with the same name (AuthorizeAttribute) in your website's default namespace (this is very important) then the compiler will automatically pick it up instead of MVC's standard one. Of course, you could always give the attribute a new name if you'd rather take that approach.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAuthenticated)
        {
            filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}
filterContext.HttpContext.User.Identity.IsAuthenticated
filterContext.HttpContext.Request.IsAuthenticated

Great idea - I've updated the answer.

> You can create a new attribute with the same name (AuthorizeAttribute) in your website's default namespace then the compiler will automatically pick it up instead of MVC's standard one. This results in an error: The type or namespace 'Authorize' could not be found ( are you missing a directive or an assembly reference?) Both using System.Web.Mvc; and the namespace for my custom AuthorizeAttribute class are referenced in the controller. To solve this I had to use [MyNamepace.Authorize]

@DePeter the spec never says anything about a redirect so why is a redirect a better solution? This alone kills ajax requests without a hack in place to solve it.

asp.net mvc - Why does AuthorizeAttribute redirect to the login page f...

asp.net-mvc authentication authorization
Rectangle 27 279

When it was first developed, System.Web.Mvc.AuthorizeAttribute was doing the right thing - older revisions of the HTTP specification used status code 401 for both "unauthorized" and "unauthenticated".

If the request already included Authorization credentials, then the 401 response indicates that authorization has been refused for those credentials.

In fact, you can see the confusion right there - it uses the word "authorization" when it means "authentication". In everyday practice, however, it makes more sense to return a 403 Forbidden when the user is authenticated but not authorized. It's unlikely the user would have a second set of credentials that would give them access - bad user experience all around.

Consider most operating systems - when you attempt to read a file you don't have permission to access, you aren't shown a login screen!

Thankfully, the HTTP specifications were updated (June 2014) to remove the ambiguity.

From "Hyper Text Transport Protocol (HTTP/1.1): Authentication" (RFC 7235):

The 401 (Unauthorized) status code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource.

From "Hypertext Transfer Protocol (HTTP/1.1): Semantics and Content" (RFC 7231):

The 403 (Forbidden) status code indicates that the server understood the request but refuses to authorize it.

Interestingly enough, at the time ASP.NET MVC 1 was released the behavior of AuthorizeAttribute was correct. Now, the behavior is incorrect - the HTTP/1.1 specification was fixed.

Rather than attempt to change ASP.NET's login page redirects, it's easier just to fix the problem at the source. You can create a new attribute with the same name (AuthorizeAttribute) in your website's default namespace (this is very important) then the compiler will automatically pick it up instead of MVC's standard one. Of course, you could always give the attribute a new name if you'd rather take that approach.

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override void HandleUnauthorizedRequest(System.Web.Mvc.AuthorizationContext filterContext)
    {
        if (filterContext.HttpContext.Request.IsAuthenticated)
        {
            filterContext.Result = new System.Web.Mvc.HttpStatusCodeResult((int)System.Net.HttpStatusCode.Forbidden);
        }
        else
        {
            base.HandleUnauthorizedRequest(filterContext);
        }
    }
}
filterContext.HttpContext.User.Identity.IsAuthenticated
filterContext.HttpContext.Request.IsAuthenticated

Great idea - I've updated the answer.

> You can create a new attribute with the same name (AuthorizeAttribute) in your website's default namespace then the compiler will automatically pick it up instead of MVC's standard one. This results in an error: The type or namespace 'Authorize' could not be found ( are you missing a directive or an assembly reference?) Both using System.Web.Mvc; and the namespace for my custom AuthorizeAttribute class are referenced in the controller. To solve this I had to use [MyNamepace.Authorize]

@DePeter the spec never says anything about a redirect so why is a redirect a better solution? This alone kills ajax requests without a hack in place to solve it.

asp.net mvc - Why does AuthorizeAttribute redirect to the login page f...

asp.net-mvc authentication authorization
Rectangle 27 23

// User was redirected here because of authorization section
if (User.Identity != null && User.Identity.IsAuthenticated)
    Response.Redirect("Unauthorized.aspx");

When the user is redirected there but is already logged in, it shows the unauthorized page. If they are not logged in, it falls through and shows the login page.

@Chance - then do that in the default ActionMethod for the controller that is called where FormsAuthencation has been setup to call.

if (User.Identity != null && User.Identity.IsAuthenticated) return RedirectToRoute("Unauthorized");

So you ask a resource, you get redirected to a login page and you get redirected again to a 403 page? Seems bad to me. I even can't tolerate one redirect at all. IMO this thing is very badly built anyway.

According to your solution, If you have already logged in and go to Login page by typing the URL ... this would throw you to Unauthorized page. which is not right.

asp.net mvc - Why does AuthorizeAttribute redirect to the login page f...

asp.net-mvc authentication authorization
Rectangle 27 23

// User was redirected here because of authorization section
if (User.Identity != null && User.Identity.IsAuthenticated)
    Response.Redirect("Unauthorized.aspx");

When the user is redirected there but is already logged in, it shows the unauthorized page. If they are not logged in, it falls through and shows the login page.

@Chance - then do that in the default ActionMethod for the controller that is called where FormsAuthencation has been setup to call.

if (User.Identity != null && User.Identity.IsAuthenticated) return RedirectToRoute("Unauthorized");

So you ask a resource, you get redirected to a login page and you get redirected again to a 403 page? Seems bad to me. I even can't tolerate one redirect at all. IMO this thing is very badly built anyway.

According to your solution, If you have already logged in and go to Login page by typing the URL ... this would throw you to Unauthorized page. which is not right.

asp.net mvc - Why does AuthorizeAttribute redirect to the login page f...

asp.net-mvc authentication authorization
Rectangle 27 23

// User was redirected here because of authorization section
if (User.Identity != null && User.Identity.IsAuthenticated)
    Response.Redirect("Unauthorized.aspx");

When the user is redirected there but is already logged in, it shows the unauthorized page. If they are not logged in, it falls through and shows the login page.

@Chance - then do that in the default ActionMethod for the controller that is called where FormsAuthencation has been setup to call.

if (User.Identity != null && User.Identity.IsAuthenticated) return RedirectToRoute("Unauthorized");

So you ask a resource, you get redirected to a login page and you get redirected again to a 403 page? Seems bad to me. I even can't tolerate one redirect at all. IMO this thing is very badly built anyway.

According to your solution, If you have already logged in and go to Login page by typing the URL ... this would throw you to Unauthorized page. which is not right.

asp.net mvc - Why does AuthorizeAttribute redirect to the login page f...

asp.net-mvc authentication authorization
Rectangle 27 4

I always thought this did make sense. If you're logged in and you try to hit a page that requires a role you don't have, you get forwarded to the login screen asking you to log in with a user who does have the role.

You might add logic to the login page that checks to see if the user is already authenticated. You could add a friendly message that explains why they've been bumbed back there again.

It's my feeling that most people don't tend to have more than one identity for a given web app. If they do, then they're smart enough to think "my current ID doesn't have mojo, I'll log back in as the other one".

Although your other point about displaying something on the login page is a good one. Thanks.

asp.net mvc - Why does AuthorizeAttribute redirect to the login page f...

asp.net-mvc authentication authorization
Rectangle 27 4

I always thought this did make sense. If you're logged in and you try to hit a page that requires a role you don't have, you get forwarded to the login screen asking you to log in with a user who does have the role.

You might add logic to the login page that checks to see if the user is already authenticated. You could add a friendly message that explains why they've been bumbed back there again.

It's my feeling that most people don't tend to have more than one identity for a given web app. If they do, then they're smart enough to think "my current ID doesn't have mojo, I'll log back in as the other one".

Although your other point about displaying something on the login page is a good one. Thanks.

asp.net mvc - Why does AuthorizeAttribute redirect to the login page f...

asp.net-mvc authentication authorization
Rectangle 27 4

Unfortunately, you're dealing with the default behavior of ASP.NET forms authentication. There is a workaround (I haven't tried it) discussed here:

I think in most cases the best solution is to restrict access to unauthorized resources prior to the user trying to get there. By removing/graying out the link or button that might take them to this unauthorized page.

It probably would be nice to have an additional parameter on the attribute to specify where to redirect an unauthorized user. But in the meantime, I look at the AuthorizeAttribute as a safety net.

I plan on removing the link based on authorization as well (I saw a question on here about that somewhere), so I'll code an HtmlHelper extension method up later.

I still have to prevent the user from going directly to the URL, which is what this attribute is all about. I'm not too happy with the Custom 401 solution (seems a bit global), so I'll try modelling my NotAuthorizedResult on RedirectToRouteResult...

asp.net mvc - Why does AuthorizeAttribute redirect to the login page f...

asp.net-mvc authentication authorization
Rectangle 27 4

Unfortunately, you're dealing with the default behavior of ASP.NET forms authentication. There is a workaround (I haven't tried it) discussed here:

I think in most cases the best solution is to restrict access to unauthorized resources prior to the user trying to get there. By removing/graying out the link or button that might take them to this unauthorized page.

It probably would be nice to have an additional parameter on the attribute to specify where to redirect an unauthorized user. But in the meantime, I look at the AuthorizeAttribute as a safety net.

I plan on removing the link based on authorization as well (I saw a question on here about that somewhere), so I'll code an HtmlHelper extension method up later.

I still have to prevent the user from going directly to the URL, which is what this attribute is all about. I'm not too happy with the Custom 401 solution (seems a bit global), so I'll try modelling my NotAuthorizedResult on RedirectToRouteResult...

asp.net mvc - Why does AuthorizeAttribute redirect to the login page f...

asp.net-mvc authentication authorization
Rectangle 27 4

I always thought this did make sense. If you're logged in and you try to hit a page that requires a role you don't have, you get forwarded to the login screen asking you to log in with a user who does have the role.

You might add logic to the login page that checks to see if the user is already authenticated. You could add a friendly message that explains why they've been bumbed back there again.

It's my feeling that most people don't tend to have more than one identity for a given web app. If they do, then they're smart enough to think "my current ID doesn't have mojo, I'll log back in as the other one".

Although your other point about displaying something on the login page is a good one. Thanks.

asp.net mvc - Why does AuthorizeAttribute redirect to the login page f...

asp.net-mvc authentication authorization
Rectangle 27 4

Unfortunately, you're dealing with the default behavior of ASP.NET forms authentication. There is a workaround (I haven't tried it) discussed here:

I think in most cases the best solution is to restrict access to unauthorized resources prior to the user trying to get there. By removing/graying out the link or button that might take them to this unauthorized page.

It probably would be nice to have an additional parameter on the attribute to specify where to redirect an unauthorized user. But in the meantime, I look at the AuthorizeAttribute as a safety net.

I plan on removing the link based on authorization as well (I saw a question on here about that somewhere), so I'll code an HtmlHelper extension method up later.

I still have to prevent the user from going directly to the URL, which is what this attribute is all about. I'm not too happy with the Custom 401 solution (seems a bit global), so I'll try modelling my NotAuthorizedResult on RedirectToRouteResult...

asp.net mvc - Why does AuthorizeAttribute redirect to the login page f...

asp.net-mvc authentication authorization
Rectangle 27 72

In your ensureAuthenticated method save the return url in the session like this:

...
req.session.returnTo = req.path; 
res.redirect('/login');
...
app.get('/auth/google/return', passport.authenticate('google'), function(req, res) {
    res.redirect(req.session.returnTo || '/');
    delete req.session.returnTo;
});

Thanks! Exactly what I was looking for. This should be the accepted answer.

Worked on the first pass, but I needed to add the following after the redirect in the passport.authenticate route to avoid being taken back to the returnTo address after subsequent logout/login: req.session.returnTo = null; See this question for additional info about passport's default logout, which, on examination of source, seems to only clear the session.user, and not the entire session.

can i simply pass around a query like ?callback=/profile instead of session

@OMGPOP you probably could, but that doesn't sound very secure, is there a reason you don't want to take advantage of the session?

Instead of req.path I would use req.originalUrl since it is a combination of baseUrl and path, see the documentation

Redirecting to previous page after authentication in node.js using pas...

node.js authentication redirect express passport.js
Rectangle 27 24

You could use a custom authentication callback as described in the last paragraph there http://passportjs.org/guide/authenticate/.

app.post('/login', function(req, res, next) {
  passport.authenticate('local', function(err, user, info) {
    if (err) { return next(err); }
    // Redirect if it fails
    if (!user) { return res.redirect('/login'); }
    req.logIn(user, function(err) {
      if (err) { return next(err); }
      // Redirect if it succeeds
      return res.redirect('/users/' + user.username);
    });
  })(req, res, next);
});

In your defense, it's a rather terse manual for a large concept. (coming across similar problems myself...)

node.js - Nodejs and PassportJs: Redirect middleware after passport.au...

node.js passport.js
Rectangle 27 4

first of all I completely removed the interceptor I was using before. then I made a function inside my Routing .config to use with every resolve for the authentication. finally to handle my resolve I'm using $stateChangeError to redirect to the login state

.config(function ($stateProvider, $urlRouterProvider) {

    // function to check the authentication //
    var Auth = ["$q", "authService", function ($q, authService) {
        authService.fillAuthData;
        if (authService.authentication.isAuth) {
            return $q.when(authService.authentication);
        } else {
            return $q.reject({ authenticated: false });
        }
    }];

    /* if the state does not exist */
    $urlRouterProvider
        .otherwise('/page-not-found'); 

    $stateProvider

        // state that allows non authenticated users //
        .state('home', {
            url: '/',
            templateUrl: '/Content/partials/home.html',
        })

        // state that needs authentication //
        .state('smo-dashboard', {
            url: '/dashboard',
            templateUrl: '/Content/partials/dashboard.html',
            resolve: {
                auth: Auth
            }
        })

        // errors //
         .state('page-not-found', {
             url: '/page-not-found',
             templateUrl: '/Content/partials/error/404.html'
         })

        // accounts //
        .state('login', {
            url: '/accounts/login',
            templateUrl: '/Content/partials/account/login.html'
        })

        // OTHER STATES //
    }
);

in the MainController

$scope.$on("$stateChangeError", function (event, toState, toParams, fromState, fromParams, error) {
    $state.go("login");
});

authentication - angularjs redirect to login page if not authenticated...

angularjs authentication redirect routing
Rectangle 27 53

I don't know about passport, but here's how I do it:

app.get('/account', auth.restrict, routes.account)
redirectTo
auth.restrict = function(req, res, next){
    if (!req.session.userid) {
        req.session.redirectTo = '/account';
        res.redirect('/login');
    } else {
        next();
    }
};

Then in routes.login.post I do the following:

var redirectTo = req.session.redirectTo ? req.session.redirectTo : '/';
delete req.session.redirectTo;
// is authenticated ?
res.redirect(redirectTo);

thanks for your reply. Aren't you setting the redirect /account direct in your code? What happens if you have another link let's say /pro_accounts using the same auth.restrict they would be redirected to /account ....

That's true. I always redirect to /account after they login, but you could replace it with req.session.redirect_to = req.path

hm.. its not quite I'm looking for. I call ensureAuthenticated to figure out if a user is already authed or not, if not it is redirected to /login. From this view I need a way to get back to /account. Calling req.path within /login gives me simple /login back. Using referer doesn't work either plus its not quite certain if the browser sets the referer properly...

You need to set req.session.redirect_to = req.path inside your auth middleware. Then read it and delete it in login.post route.

hey chovy, that worked perfectly fine.

Redirecting to previous page after authentication in node.js using pas...

node.js authentication redirect express passport.js
Rectangle 27 23

On the Page_Load of your login page, you'll want to check if the user is authenticated, and if they are to redirect them to your access denied page:

If you want to get a little fancier, you can check the ReturnUrl parameter to determine if the user came to the page directly (such as through a bookmark they saved right to the login page) and handle that differently. Here's an example:

protected void Page_Load(object sender, EventArgs e)
    {
        if (User.Identity.IsAuthenticated)
        {

            // if they came to the page directly, ReturnUrl will be null.
            if (String.IsNullOrEmpty(Request["ReturnUrl"]))
            {
                 /* in that case, instead of redirecting, I hide the login 
                    controls and instead display a message saying that are 
                    already logged in. */
            }
            else
            {
            Response.Redirect("~/AccessDenied.aspx");
            }
        }
    }

+1 Coz this solution will work. But is this the best solution ?

in this case the authenticated user will be redirected to AccessDenied page... but this will apply to all the users. what if I am administrator and I need access to the page ? if I try to access the page then it will redirect me to AccessDenied anyway... !!!

@Lucky - You could put another check in there to see if the user is an administrator, and if they are, do something else instead of redirecting to the access denied page.

authentication - How to Redirect Users to an ASP.NET page when not Aut...

asp.net authentication authorization
Rectangle 27 1

That's a nice article, but it's not exactly what I'm trying to do. My problem is that when A.aspx is requested, the user gets sent to a login page if not authenticated, then redirects to their original request and NOT a hard-coded page. Problem is the GetRedirectURL is always default.aspx and not the original A.aspx requested.

@Bob - if you don't have a DefaultUrl it will just choose default.aspx as a matter of the framework. When FormsAuthentication uses its own mechanism, a RedirectUrl is appended to the url to login.aspx and the user is redirected properly at that point. If no RedirectUrl is specified, the DefaultUrl is used so it can't hurt to have it specified. I think the problem here is that you're using your own session code to perform this redirect instead of allowing FormsAuth to handle it.

That sounds right. I must not be doing the forms authentication properly and the default.aspx is coming from the framework - thanks I'll see what I can do.

asp.net forms authentication redirect problem - Stack Overflow

asp.net
Rectangle 27 60

ASP.NET 4.5 added the Boolean HttpResponse.SuppressFormsAuthenticationRedirect property.

public void ProcessRequest(HttpContext context)
{
    Response.StatusCode = 401;
    Response.StatusDescription = "Authentication required";
    Response.SuppressFormsAuthenticationRedirect = true;
}

This is the answer for anyone using .NET 4.5. Too bad I didn't read this far in the list and I had to find this on my own in the decompiled sources!

You can put this in the Global.asax Application_EndRequest event to apply this across your entire application. For Example.

I used a similar snippet in the Application_BeginRequest method with great success. Is the request AJAX? Set the flag immediately.

Thanks, I'm marking this as the accepted answer now, since this definitely looks like the way to go now.

finding this useful in a migration from FormsAuth to ASP.NET Identity where I need both active at the same time

asp.net - Forms authentication: disable redirect to the login page - S...

asp.net forms-authentication
Rectangle 27 12

thx jared, I'll give it a try

Redirecting to previous page after authentication in node.js using pas...

node.js authentication redirect express passport.js