web-dev-qa-db-ja.com

MVC Identity(2.0.1)のregenerateIdentity / validateInterval期間の後にExpireTimeSpanが無視される

これで一日中頭を悩ませてきました。 MVC Identity 2.0.1で「非常に長い」ログインセッションを設定しようとしています。 (30日)。

次のCookieスタートアップを使用します。

      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))
            }
        });

全体として、うまく機能します。 Cookieは30日後に設定されているため、すべて問題ありません。

"validateInterval"の時間が経過した後(ここでは30分)ブラウザーを閉じて戻ってきた場合、まだログインしていますが、Cookieは "セッション"のみとして再発行されます(正しいCookie名はまだです)。 30日間の有効期限が切れています。

ブラウザを閉じて再度開くと、ログインできなくなります。

「プロバイダー」の削除をテストしたところ、すべてが期待どおりに機能しました。数時間後に戻ってくることができますが、それでもログインできます。しかし、スタンプの再検証を使用するのがベストプラクティスであることを読んだので、続行する方法がわかりません。

29
GregTheDev

SecurityStampValidatorregenerateIdentityコールバックを起動すると、現在認証されているユーザーがnon-persistentログインで再サインインします。これはハードコーディングされており、直接制御する方法はないと思います。そのため、ログインセッションは、IDが再生成された時点で実行しているブラウザセッションの最後まで続行されます。

これは、ID再生成操作をまたいでログインを永続化するためのアプローチです。この説明は、Visual Studio MVC ASP.NET Webプロジェクトテンプレートの使用に基づいています。

最初に、ログインセッションが個別のHTTPリクエスト間で永続的であるという事実を追跡する方法が必要です。これは、ユーザーのIDに "IsPersistent"クレームを追加することで実行できます。次の拡張メソッドは、これを行う方法を示しています。

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);
        }
    }
}

次に、ユーザーが永続的なセッションをリクエストするためにサインインするときに、「IsPersistent」クレームを作成する必要があります。たとえば、ApplicationUserクラスにはGenerateUserIdentityAsyncメソッドがあり、次のように更新してisPersistentフラグパラメータを取得し、必要なときにこのような要求を行うことができます。

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

ApplicationUser.GenerateUserIdentityAsyncの呼び出し元は、isPersistentフラグを渡す必要があります。たとえば、AccountController.SignInAsyncでのGenerateUserIdentityAsyncの呼び出しは、

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

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

最後に、CookieAuthenticationProvider.OnValidateIdentityメソッドで使用されるStartup.ConfigureAuthデリゲートは、ID再生成操作全体で永続性の詳細を維持するために注意が必要です。デフォルトのデリゲートは次のようになります。

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

これは次のように変更できます。

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

このバグはASP.NET Identity 2.2で修正されています。参照 https://aspnetidentity.codeplex.com/workitem/2319

4
robrich