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".