web-dev-qa-db-fra.com

ExpireTimeSpan ignoré après la durée de régénérationIdentité / validateInterval dans MVC Identity (2.0.1)

Je me suis gratté la tête toute la journée sur celui-ci. J'essaie de configurer des sessions de connexion "très longues" dans MVC Identity 2.0.1. (30 jours).

J'utilise le démarrage de cookie suivant:

      app.UseCookieAuthentication(new CookieAuthenticationOptions
        {

            SlidingExpiration = true,
            ExpireTimeSpan = System.TimeSpan.FromDays(30),
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/My/Login"),
            CookieName = "MyLoginCookie",
            Provider = new CookieAuthenticationProvider
            {                           
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                    validateInterval: TimeSpan.FromMinutes(30),

                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
            }
        });

Ce qui, dans l'ensemble, fonctionne bien. Le cookie est défini dans 30 jours, tout semble bien.

Si je ferme le navigateur et que je reviens après la durée de "validateInterval" (30 minutes ici), je suis toujours connecté, mais le cookie est maintenant réémis uniquement en "session" (toujours le nom correct du cookie)! L'expiration de 30 jours est révolue.

Si je ferme maintenant le navigateur/rouvre à nouveau, je ne suis plus connecté.

J'ai testé la suppression du "Provider" et tout fonctionne comme prévu alors, je peux revenir plusieurs heures plus tard et je suis toujours bien connecté. J'ai lu qu'il est préférable d'utiliser la revalidation du tampon, donc je ne sais pas comment procéder.

29
GregTheDev

Lorsque SecurityStampValidator déclenche le rappel regenerateIdentity, l'utilisateur actuellement authentifié se reconnecte avec une connexion non persistante. C'est codé en dur, et je ne pense pas qu'il existe un moyen de le contrôler directement. En tant que tel, la session de connexion se poursuivra uniquement jusqu'à la fin de la session du navigateur que vous exécutez au moment où l'identité est régénérée.

Voici une approche pour rendre la connexion persistante, même à travers les opérations de régénération d'identité. Cette description est basée sur l'utilisation de modèles de projet Web ASP.NET Visual Studio MVC.

Nous devons d'abord avoir un moyen de suivre le fait qu'une session de connexion est persistante sur des requêtes HTTP distinctes. Cela peut être fait en ajoutant une revendication "IsPersistent" à l'identité de l'utilisateur. Les méthodes d'extension suivantes montrent comment procéder.

public static class ClaimsIdentityExtensions
{
    private const string PersistentLoginClaimType = "PersistentLogin";

    public static bool GetIsPersistent(this System.Security.Claims.ClaimsIdentity identity)
    {
        return identity.Claims.FirstOrDefault(c => c.Type == PersistentLoginClaimType) != null;
    }

    public static void SetIsPersistent(this System.Security.Claims.ClaimsIdentity identity, bool isPersistent)
    {
        var claim = identity.Claims.FirstOrDefault(c => c.Type == PersistentLoginClaimType);
        if (isPersistent)
        {
            if (claim == null)
            {
                identity.AddClaim(new System.Security.Claims.Claim(PersistentLoginClaimType, Boolean.TrueString));
            }
        }
        else if (claim != null)
        {
            identity.RemoveClaim(claim);
        }
    }
}

Ensuite, nous devons effectuer la revendication "IsPersistent" lorsque l'utilisateur se connecte pour demander une session persistante. Par exemple, votre classe ApplicationUser peut avoir une méthode GenerateUserIdentityAsync qui peut être mise à jour pour prendre un paramètre indicateur isPersistent comme suit pour faire une telle réclamation en cas de besoin:

public async Task<ClaimsIdentity> GenerateUserIdentityAsync(UserManager<ApplicationUser> manager, bool isPersistent)
{
    var userIdentity = await manager.CreateIdentityAsync(this, DefaultAuthenticationTypes.ApplicationCookie);
    userIdentity.SetIsPersistent(isPersistent);
    return userIdentity;
}

Tous les appelants de ApplicationUser.GenerateUserIdentityAsync devra maintenant passer le drapeau isPersistent. Par exemple, l'appel à GenerateUserIdentityAsync dans AccountController.SignInAsync changerait de

AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, 
    await user.GenerateUserIdentityAsync(UserManager));

à

AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent },
    await user.GenerateUserIdentityAsync(UserManager, isPersistent));

Enfin, le CookieAuthenticationProvider.OnValidateIdentity délégué utilisé dans le Startup.ConfigureAuth La méthode a besoin d'une certaine attention pour préserver les détails de persistance à travers les opérations de régénération d'identité. Le délégué par défaut ressemble à:

OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
    validateInterval: TimeSpan.FromMinutes(20),
    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))

Cela peut être changé en:

OnValidateIdentity = async (context) =>
{
    await SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
        validateInterval: TimeSpan.FromMinutes(20),
        // Note that if identity is regenerated in the same HTTP request as a logoff attempt,
        // the logoff attempt will have no effect and the user will remain logged in.
        // See https://aspnetidentity.codeplex.com/workitem/1962
        regenerateIdentity: (manager, user) =>
            user.GenerateUserIdentityAsync(manager, context.Identity.GetIsPersistent())
    )(context);

    // If identity was regenerated by the stamp validator,
    // AuthenticationResponseGrant.Properties.IsPersistent will default to false, leading
    // to a non-persistent login session. If the validated identity made a claim of being
    // persistent, set the IsPersistent flag to true so the application cookie won't expire
    // at the end of the browser session.
    var newResponseGrant = context.OwinContext.Authentication.AuthenticationResponseGrant;
    if (newResponseGrant != null)
    {
        newResponseGrant.Properties.IsPersistent = context.Identity.GetIsPersistent();
    }
}
36
chrisg

Ce bogue est corrigé dans ASP.NET Identity 2.2. Voir https://aspnetidentity.codeplex.com/workitem/2319

4
robrich