49

I have been working on a web application using MVC5, EF6, and VS 2013.

I spent some time upgrading to the RC bits once released. Thanks to all the great posts out there: eg. Decoupling Microsoft.AspNet.Identity.* and Updating asp.net MVC from 5.0.0-beta2 to 5.0.0-rc1 !

In my infinite wisdom, I decided to move to the RTM bits that @Hao Kung posted about here: How can I get early access to upcoming Asp.Net Identity changes?. I figured I would save the trouble and not be too far behind when we finally receive the RTM build.

This has either been a nightmare, or I am just completely missing something (or both) as I can not figure out basic tasks that had been working with the RC1 stuff.

While it seems like I am logging the user in via the controller (Where is Microsoft.AspNet.Identity.Owin.AuthenticationManager in Asp.Net Identity RTM version?) ... my WindowsIdentity is always empty and not authenticated after I call SignIn. The user and claimsIdentity object are correctly populated.

Here is the action method I am calling (moved properties to local variables for completeness):

[HttpPost, AllowAnonymous, ValidateAntiForgeryToken]
public virtual async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
{
    if (ModelState.IsValid) {
        var userManager = new UserManager<EtdsUser>(new UserStore<EtdsUser>(new EtdsContext()));
        var user = userManager.Find(model.UserName, model.Password);
        if (user != null) {
            var authenticationManager = HttpContext.GetOwinContext().Authentication;
            authenticationManager.SignOut(new[] {DefaultAuthenticationTypes.ApplicationCookie, DefaultAuthenticationTypes.ExternalCookie, DefaultAuthenticationTypes.ExternalBearer});
            var claimsIdentity = await userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
            authenticationManager.SignIn(new AuthenticationProperties { IsPersistent = model.RememberMe}, claimsIdentity);
            return RedirectToLocal(returnUrl);
        }
    }
    ModelState.AddModelError("", "The user name or password provided is incorrect.");
    return View(model);
}

(On a side note: I do not need to log in external users at this time.)

Any suggestions? -or- Should I roll back all my changes and just wait until VS 2013 is RTMd?


Update, refactored code to make it closer to @Hao Kung's original reply. However I still do not end up with a valid user identity. I think my AuthenticationManager is not assigned correctly?

AuthenticationManger is now defined as:

public IAuthenticationManager AuthenticationManager { get { return HttpContext.GetOwinContext().Authentication; } }

SignInAsync is now a separate method:

private async Task SignInAsync(EtdsUser user, bool isPersistent)
{
    AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
    var claimsIdentity = await _userManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
    AuthenticationManager.SignIn(new AuthenticationProperties { IsPersistent = isPersistent}, claimsIdentity);
}

After "SignOut", the debugger shows:

AuthenticationManager.User.Identity
{System.Security.Principal.WindowsIdentity}
    [System.Security.Principal.WindowsIdentity]: {System.Security.Principal.WindowsIdentity}
    AuthenticationType: ""
    IsAuthenticated: false
    Name: ""

The "claimsIdentity" is then:

claimsIdentity
{System.Security.Claims.ClaimsIdentity}
    Actor: null
    AuthenticationType: "ApplicationCookie"
    BootstrapContext: null
    Claims: {System.Security.Claims.ClaimsIdentity.get_Claims}
    IsAuthenticated: true
    Label: null
    Name: "alon"
    NameClaimType: "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"
    RoleClaimType: "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"

"SignIn" does not change anything:

AuthenticationManager.User.Identity
{System.Security.Principal.WindowsIdentity}
    [System.Security.Principal.WindowsIdentity]: {System.Security.Principal.WindowsIdentity}
    AuthenticationType: ""
    IsAuthenticated: false
    Name: ""

Still no Authentication, but seems that no errors are thrown.


As answered by @Hao Kung, changed StartUp.Auth.cs from:

var authOptions = new CookieAuthenticationOptions { ExpireTimeSpan = TimeSpan.FromHours(4.0)};
app.UseCookieAuthentication(authOptions);

To:

var authOptions = new CookieAuthenticationOptions {
    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
    LoginPath = new PathString("/Account/Login"),
    ExpireTimeSpan = TimeSpan.FromHours(4.0)
}; ...
starball
  • 20,030
  • 7
  • 43
  • 238
ACG
  • 750
  • 2
  • 6
  • 16
  • There is an interface... Microsoft.Owin.Security.IAuthenticationManager – user2315985 Sep 30 '13 at 16:23
  • Doesn't HttpContext.GetOwinContext().Authentication return an implementation of IAuthenticationManager? At least I thought it did, but I may be missing something. – ACG Oct 01 '13 at 10:33

1 Answers1

41

So here's what login will basically look like in RTM (code copied from the ASPNET Identity sample code):

    //
    // POST: /Account/Login
    [HttpPost]
    [AllowAnonymous]
    [ValidateAntiForgeryToken]
    public async Task<ActionResult> Login(LoginViewModel model, string returnUrl)
    {
        if (ModelState.IsValid)
        {
            var user = await UserManager.FindAsync(model.UserName, model.Password);
            if (user != null)
            {
                await SignInAsync(user, model.RememberMe);
                return RedirectToLocal(returnUrl);
            }
            else
            {
                ModelState.AddModelError("", "Invalid username or password.");
            }
        }

        // If we got this far, something failed, redisplay form
        return View(model);
    }

    private async Task SignInAsync(ApplicationUser user, bool isPersistent)
    {
        AuthenticationManager.SignOut(DefaultAuthenticationTypes.ExternalCookie);
        var identity = await UserManager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
        AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
    }

EDIT: And you need the follow changes in your Startup.Auth.cs:

        app.UseCookieAuthentication(new CookieAuthenticationOptions {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login")
        });
George Stocker
  • 57,289
  • 29
  • 176
  • 237
Hao Kung
  • 28,040
  • 6
  • 84
  • 93
  • Awesome thanks ... just curious, do my instantiations of "userManager" and "authenticationManager" look correct? I modified my Login method based on your previous post, so I hope I was sort of going down the right path. – ACG Oct 01 '13 at 07:09
  • I refactored my Login method and created SignInAsync as you have suggested. My code now looks similar to yours (and the original post you made). The _userManager.FindAsync method returns my correct custom User (EtdsUser). However after calling AuthenticationManager.SignIn ... the User.Identity is still empty. In your previous reply to a similar question, you stated "That most likely means something went wrong with the sign in, and no claims principal is there". _userManager.CreateIdentityAsync does return a valid identity, I just can't actually get it "signed" in. – ACG Oct 01 '13 at 07:28
  • See my edited question above with additional details. – ACG Oct 01 '13 at 07:40
  • 2
    I added the change that you probably need in startup.auth.cs as well – Hao Kung Oct 01 '13 at 16:51
  • That was it. Basically pretty annoying ;-) Just needed to add 1/2 lines. Thanks! – ACG Oct 02 '13 at 06:33
  • I do not have a method GetOwinContext() available via HttpContext.GetOwinContext()... any idea why not? – user2315985 Oct 02 '13 at 18:25
  • The last EDIT - setting the app.UseCookieAuthentication(...) solved it for me – Andrew dh May 04 '14 at 04:46