Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.net Identity stops handing out External Cookie after removing external account

I have a site setup with four 3rd-party login services, Microsoft, VS, Github, and Linkedin. Everything seems to work great, I can log in/out, add/remove external accounts with no problem.

Randomly however, it seems to stop working. When I try to login using any of the 3rd-party services, it just kicks me back to the login page.

Looking at the ExternalLoginCallback it appears that the AuthenticateResult.Identity is null and it can't get the external login info. Looking at it on the client-side it looks like they never got the external signin cookie.

I still can't consistently reproduce this error, so it's really hard to determine what might be happening. Any help would be great.

Update 1: I was able to identify the steps to reproduce:

  1. Login to an account with more than 1 associated login
  2. Remove one of the logins
  3. In a new browser or a private session, try to log in with any of the 3rd-party accounts and you will be returned to login without an external cookie.

After hitting the error it won't hand out a cookie to any new sessions until IIS is restarted.

Update 2: Looks like it has something to do with setting a Session variable.

On the removeLogin action I was adding a value to the session. I'm not sure why but when I stopped doing that, I stopped having my problem. Time to figure out why... Update 3: Looks like this problem has been reported to the Katana Team

Update 4: Looks like someone else already ran into this problem. Stackoverflow post. They didn't give all of the code you needed to solve it, so I'll include that here as an answer.


Startup.Auth.cs

public void ConfigureAuth(IAppBuilder app) {

    // Configure the db context, user manager and signin manager to use a single instance per request
    app.CreatePerOwinContext(appContext.Create);
    app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
    app.CreatePerOwinContext<ApplicationRoleManager>(ApplicationRoleManager.Create);
    app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);

    // Enable the application to use a cookie to store information for the signed in user
    // and to use a cookie to temporarily store information about a user logging in with a third party login provider
    // Configure the sign in cookie
    app.UseCookieAuthentication(new CookieAuthenticationOptions {
        AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
        AuthenticationMode = AuthenticationMode.Active,
        LoginPath = new PathString("/Login"),
        LogoutPath = new PathString("/Logout"),
        Provider = new CookieAuthenticationProvider {
            // Enables the application to validate the security stamp when the user logs in.
            // This is a security feature which is used when you change a password or add an external login to your account.  
            OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, User, int>(
                validateInterval: TimeSpan.FromMinutes(30),
                regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
                getUserIdCallback: (id) => (Int32.Parse(id.GetUserId()))
            )
        }
    });
    app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

    // Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
    app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));

    // Enables the application to remember the second login verification factor such as phone or email.
    // Once you check this option, your second step of verification during the login process will be remembered on the device where you logged in from.
    // This is similar to the RememberMe option when you log in.
    app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);

    // Uncomment the following lines to enable logging in with third party login providers
    app.UseMicrosoftAccountAuthentication(new MicrosoftAccountAuthenticationOptions{
        ClientId = ConfigurationManager.AppSettings["MSA:Id"],
        ClientSecret = ConfigurationManager.AppSettings["MSA:Secret"],
        Caption = "Microsoft"
    });

    app.UseVisualStudioAuthentication(new VisualStudioAuthenticationOptions(){
        AppId = ConfigurationManager.AppSettings["VSO:Id"],
        AppSecret = ConfigurationManager.AppSettings["VSO:Secret"],
        Provider = new VisualStudioAuthenticationProvider(){
            OnAuthenticated = (context) =>{
                context.Identity.AddClaim(new Claim("urn:vso:access_token", context.AccessToken, XmlSchemaString, "VisualStudio"));
                context.Identity.AddClaim(new Claim("urn:vso:refresh_token", context.RefreshToken, XmlSchemaString, "VisualStudio"));
                return Task.FromResult(0);
            }
        },
        Caption = "Visual Studio"
    });

    app.UseGitHubAuthentication(new GitHubAuthenticationOptions{
        ClientId = ConfigurationManager.AppSettings["GH:Id"],
        ClientSecret = ConfigurationManager.AppSettings["GH:Secret"],
        Caption = "Github"
    });

    app.UseLinkedInAuthentication(new LinkedInAuthenticationOptions {
        ClientId = ConfigurationManager.AppSettings["LI:Id"],
        ClientSecret = ConfigurationManager.AppSettings["LI:Secret"],
        Caption = "LinkedIn"
    });
}
like image 808
JustMaier Avatar asked Apr 27 '15 21:04

JustMaier


1 Answers

OWIN and asp.net handle cookies/session differently. If you authorize with OWIN before you initialize a session, anyone after a session is initialized will not be able to login.

Workaround: Add the following to your Global.asax

// Fix for OWIN session bug
    protected void Application_AcquireRequestState() {
        Session["Workaround"] = 0;
    }
}

Long term: The way OWIN and asp.net handle sessions/cookies will be merged in vNext, use the work around until then...

like image 84
JustMaier Avatar answered Oct 04 '22 04:10

JustMaier