Brock Allen MembershipReboot with SPA(Single Page Application) Breeze Application Authorization Redirect

273 Views Asked by At

I have integrated MembershipReboot with a Breeze SPA application and the login and authorisztion work as expected. In the BreezeController.cs I have added the code below to capture an authorization failure.

[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
    public class AuthorizeAttribute : System.Web.Http.Filters.AuthorizationFilterAttribute
    {

        public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
        {
            base.OnAuthorization(actionContext);

            ////check authentication and return if not authorized
            if (actionContext != null)
            {
                if (!actionContext.RequestContext.Principal.Identity.IsAuthenticated)
                {
                    actionContext.Response = actionContext.ControllerContext.Request.CreateResponse(System.Net.HttpStatusCode.Redirect);
                    System.Web.HttpContext.Current.Server.ClearError();
                    System.Web.HttpContext.Current.Response.Redirect("/UserAccount/Home",true);
                    //***********
                    //REDIRECT BEING CAUGHT BY ANGULAR ERROR HANDLER!!!
                    //**********
                    System.Web.HttpContext.Current.ApplicationInstance.CompleteRequest();


                }

            }
        }
    }

The lack of Authorization is caught when the code below is called:

[System.Web.Http.HttpGet] [ValidateAntiForgeryToken] [Authorize] public string Metadata() { return _repository.Metadata; }

However the redirect code is being loaded into the Toast error handler and displayed as an error and the redirect is not working.

Any ideas how I can get the code to run as opposed to being loaded into the error screen?

2

There are 2 best solutions below

1
On

Handle the failed promise and examine the error object. You should find the status code in there that tells you this is an authorization failure. Now redirect as appropriate for your app rather than reporting the error to screen.

I think I'd put this "interceptor" inside my "DataService"/"DataContext" abstraction so that all Breeze calls make use of it. Who know, you might extend the EntityManager with it. Haven't thought about it much.

You might want to share this "interceptor" with all of us when you get it working. The community loves contributions. :-)

0
On

I noticed app/config.exceptionHandler.js is trapping all my errors creating log errors. I have looked for exception 401 (unauthorised access) and if spotted called my login module.

The code is simply:

var app = angular.module('app');

// Configure by setting an optional string value for appErrorPrefix.
// Accessible via config.appErrorPrefix (via config value).

app.config(['$provide', function ($provide) {
    $provide.decorator('$exceptionHandler',
        ['$delegate', 'config', 'logger', extendExceptionHandler]);
}]);

// Extend the $exceptionHandler service to also display a toast.
function extendExceptionHandler($delegate, config, logger) {
    var appErrorPrefix = config.appErrorPrefix;
    var logError = logger.getLogFn('app', 'error');
    return function (exception, cause) {
        $delegate(exception, cause);
        if (exception.status == 401) {
            window.location.href = "/UserAccount/Home";
        }
        if (exception.message == "undefined") { return; }
        if (appErrorPrefix && exception.message.indexOf(appErrorPrefix) === 0) { return; }

        var errorData = { exception: exception, cause: cause };
        var msg = appErrorPrefix + exception.message;
        logError(msg, errorData, true);
    };
}

})();

The BreezeController.cs was modified to generate the 401 error like so:

[AttributeUsageAttribute(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class AuthorizeAttribute : System.Web.Http.Filters.AuthorizationFilterAttribute
{
    public override void OnAuthorization(System.Web.Http.Controllers.HttpActionContext actionContext)
    {
        base.OnAuthorization(actionContext);

        ////check authentication and return if not authorized
        if (actionContext != null)
        {
            if (!actionContext.RequestContext.Principal.Identity.IsAuthenticated)
            {
            actionContext.Response = 
                new HttpResponseMessage(System.Net.HttpStatusCode.Unauthorized) { RequestMessage = actionContext.ControllerContext.Request };
            }
        }
    }
}

The entries in the controller were given the attributes [Authorize] to trap any unauthorized access like so:

    [System.Web.Http.HttpGet]
    [ValidateAntiForgeryToken]
    [Authorize]
    public string Metadata()
    {
    return _repository.Metadata;
    }

It might be dirty but it does the trick.