Login to the MVC 5 ASP.NET template Web Application fails when moved from the root folder

asp.net, asp.net-identity, asp.net-mvc

Solution

I'm facing same problem, the change I did was:

In the file app.viewmodel.js I added a new parameter (returnUrl):

// no token - so bounce to Authorize endpoint in AccountController to sign in or register
window.location = "/Account/Authorize?client_id=web&response_type=token&state=" 
    + encodeURIComponent(window.location.hash) 
    + "&returnUrl=" + encodeURIComponent(window.location);

In the method `ValidateClientRedirectUri` of class `ApplicationOAuthProvider` I read this parameter and set as the return url:

public override Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
    if (context.ClientId == _publicClientId)
    {
        Uri expectedRootUri;
        if (!string.IsNullOrEmpty(context.Request.Query["returnUrl"]))
        {
            expectedRootUri = new Uri(context.Request.Query["returnUrl"]);
        }
        else
        {
            expectedRootUri = new Uri(context.Request.Uri, "/");
        }

        if (expectedRootUri.AbsoluteUri == context.RedirectUri)
        {
            context.Validated();
        }
        else if (context.ClientId == "web")
        {
            var expectedUri = new Uri(context.Request.Query["returnUrl"]);
            context.Validated(expectedUri.AbsoluteUri);
        }
    }

    return Task.FromResult<object>(null);
}

Problem

I used the ASP.NET Web Application template to create a new "Single Page Application" with "Authentication: Individual User Accounts". It will run with the default settings without any problem. If I don't deploy the application to the root folder of the web server the authentication fails. The culprit is in the app.viewmodel.js file where the following code can be found: ``` self.addViewModel = function (options) { var viewItem = new options.factory(self, dataModel), navigator; // Add view to AppViewModel.Views enum (for example, app.Views.Home). self.Views[options.name] = viewItem; // Add binding member to AppViewModel (for example, app.home); self[options.bindingMemberName] = ko.computed(function () { if (!dataModel.getAccessToken()) { // The following code looks for a fragment in the URL to get the access token which will be // used to call the protected Web API resource var fragment = common.getFragment(); if (fragment.access_token) { // returning with access token, restore old hash, or at least hide token window.location.hash = fragment.state || ''; dataModel.setAccessToken(fragment.access_token); } else { // no token - so bounce to Authorize endpoint in AccountController to sign in or register window.location = "/Account/Authorize?client_id=web&response_type=token&state=" + encodeURIComponent(window.location.hash); } } return self.Views[options.name]; }); ``` The line where `window.location = "/Account..."` redirects the browser to an URL offset at the root directory. Unfortunately just hard coding this to the new folder instead (which I would like to avoid anyway) does not solve the problem entirely. The redirect seems to work at first but behind the scenes in the `AccountController.cs`file `Authorize()`is called which in turn calls `AuthenticationManager.SignIn(identity)` and somehere there is magic going on. There is a redirect to `http://localhost/foo/Account/Login?ReturnUrl=...` and we're back where we started. I am probably missing the obvious. I'd appreciate any pointers. It's very easy to replicate. Just create a new web app with the default settings and then go into project properties and change the "Project Url" to something like `http://localhost:49725/foo` which moves the app to a new folder called "foo".

Original source