angularjsasp.net-mvc-5.2authorize-attributecookie-authentication

AngularJs + ASP.NET MVC 5 - Handle BE redirection (HTTP code 302)


In my ASP.NET MVC 5 I use also AngularJS and my stateProvider contains also these nested states:

$stateProvider
.state("data",
{
    url: "/data",
    templateUrl: "Template/data",
    controller: "dataController"
})
.state("data.values",
{
    url: "/values",
    templateUrl: "Template/Values",
    controller: "valuesController"
})
.state("data.values.chart",
{
    url: "/chart",
    templateUrl: "Template/Chart",
    controller: "chartController"
})

On the BE side I use CookieAuthentication with the following settings:

app.UseCookieAuthentication(new CookieAuthenticationOptions
{
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/Account/Login")
});

And an attribute to verify the user against a service

public class AuthorizeAdminUserMvcAttribute : System.Web.Mvc.AuthorizeAttribute
{
    protected override bool AuthorizeCore(HttpContextBase httpContext)
    {
        var userService = DependencyResolver.Current.GetService<IUserService>();
        var authenticationManager = DependencyResolver.Current.GetService<IAuthenticationManager>();
        var currentUser = httpContext.User;
        if (string.IsNullOrEmpty(currentUser?.Identity?.Name) || userService.IsUserLockedOrMissing(currentUser?.Identity?.Name))
        {
            authenticationManager.SignOut();
            return false;
        }

        return true;
    }
} 

When I try to access /data/values/chart in the application, three requests are sent to the BE side

and for all three I get the same response: HTML code of the Login page with status 200.

To verify this behaviour I created an interceptor.

app.service("httpInterceptor", ["$q", function ($q) {
    return {
        'request': function (config) {
            // do something on success
            console.log(config);
            return config;
        },
        'requestError': function (rejection) {
            // do something on error
            console.log(rejection);
            return $q.reject(rejection);
        },
        'response': function (response) {
            // do something on success
            console.log(response);
            return response;
        },
        'responseError': function (rejection) {
            // do something on error
            console.log(rejection);
            return $q.reject(rejection);
        }
    };
}]);

There are two consequences, which I need to resolve:

  1. The url is https://localhost/#/data/values/chart, I need https://localhost/Account/Login
  2. Because the complete Login page is returned (including header), the new page seems to have two headers, I need just a single one enter image description here

Every help is highly appreciated.


Solution

  • At the end the solution was very easy - to move handling of 401 response completely from BE to FE, which means to

    app.UseCookieAuthentication(new CookieAuthenticationOptions
    {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        //LoginPath = new PathString("/Account/Login"), <- this line is commented out
    });
    
    portalApp.service("authInterceptor", ["$q", "$window", function ($q, $window) {
        this.responseError = function (response) {
            if (response.status === 401) {
                $window.location.href = "account/login";
                return;
            }
            return $q.reject(response);
        }
    }]);