Add RefreshSignIn

This commit is contained in:
Hao Kung 2015-04-23 22:17:24 -07:00
parent 791173fda8
commit c251bf0162
7 changed files with 81 additions and 32 deletions

View File

@ -72,7 +72,7 @@ namespace IdentitySample.Controllers
var result = await UserManager.RemoveLoginAsync(user, loginProvider, providerKey); var result = await UserManager.RemoveLoginAsync(user, loginProvider, providerKey);
if (result.Succeeded) if (result.Succeeded)
{ {
await SignInManager.SignInAsync(user, isPersistent: false); await SignInManager.RefreshSignInAsync(user);
message = ManageMessageId.RemoveLoginSuccess; message = ManageMessageId.RemoveLoginSuccess;
} }
} }
@ -113,7 +113,7 @@ namespace IdentitySample.Controllers
if (user != null) if (user != null)
{ {
await SignInManager.RememberTwoFactorClientAsync(user); await SignInManager.RememberTwoFactorClientAsync(user);
await SignInManager.SignInAsync(user, isPersistent: false); await SignInManager.RefreshSignInAsync(user);
} }
return RedirectToAction("Index", "Manage"); return RedirectToAction("Index", "Manage");
} }
@ -138,8 +138,7 @@ namespace IdentitySample.Controllers
if (user != null) if (user != null)
{ {
await UserManager.SetTwoFactorEnabledAsync(user, true); await UserManager.SetTwoFactorEnabledAsync(user, true);
// TODO: flow remember me somehow? await SignInManager.RefreshSignInAsync(user);
await SignInManager.SignInAsync(user, isPersistent: false);
} }
return RedirectToAction("Index", "Manage"); return RedirectToAction("Index", "Manage");
} }
@ -154,7 +153,7 @@ namespace IdentitySample.Controllers
if (user != null) if (user != null)
{ {
await UserManager.SetTwoFactorEnabledAsync(user, false); await UserManager.SetTwoFactorEnabledAsync(user, false);
await SignInManager.SignInAsync(user, isPersistent: false); await SignInManager.RefreshSignInAsync(user);
} }
return RedirectToAction("Index", "Manage"); return RedirectToAction("Index", "Manage");
} }
@ -187,7 +186,7 @@ namespace IdentitySample.Controllers
var result = await UserManager.ChangePhoneNumberAsync(user, model.PhoneNumber, model.Code); var result = await UserManager.ChangePhoneNumberAsync(user, model.PhoneNumber, model.Code);
if (result.Succeeded) if (result.Succeeded)
{ {
await SignInManager.SignInAsync(user, isPersistent: false); await SignInManager.RefreshSignInAsync(user);
return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess }); return RedirectToAction("Index", new { Message = ManageMessageId.AddPhoneSuccess });
} }
} }
@ -207,7 +206,7 @@ namespace IdentitySample.Controllers
var result = await UserManager.SetPhoneNumberAsync(user, null); var result = await UserManager.SetPhoneNumberAsync(user, null);
if (result.Succeeded) if (result.Succeeded)
{ {
await SignInManager.SignInAsync(user, isPersistent: false); await SignInManager.RefreshSignInAsync(user);
return RedirectToAction("Index", new { Message = ManageMessageId.RemovePhoneSuccess }); return RedirectToAction("Index", new { Message = ManageMessageId.RemovePhoneSuccess });
} }
} }
@ -238,7 +237,7 @@ namespace IdentitySample.Controllers
var result = await UserManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword); var result = await UserManager.ChangePasswordAsync(user, model.OldPassword, model.NewPassword);
if (result.Succeeded) if (result.Succeeded)
{ {
await SignInManager.SignInAsync(user, isPersistent: false); await SignInManager.RefreshSignInAsync(user);
return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess }); return RedirectToAction("Index", new { Message = ManageMessageId.ChangePasswordSuccess });
} }
AddErrors(result); AddErrors(result);
@ -272,7 +271,7 @@ namespace IdentitySample.Controllers
var result = await UserManager.AddPasswordAsync(user, model.NewPassword); var result = await UserManager.AddPasswordAsync(user, model.NewPassword);
if (result.Succeeded) if (result.Succeeded)
{ {
await SignInManager.SignInAsync(user, isPersistent: false); await SignInManager.RefreshSignInAsync(user);
return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess }); return RedirectToAction("Index", new { Message = ManageMessageId.SetPasswordSuccess });
} }
AddErrors(result); AddErrors(result);

View File

@ -7,6 +7,7 @@ using Microsoft.Data.Entity;
using Microsoft.Framework.ConfigurationModel; using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging; using Microsoft.Framework.Logging;
using Microsoft.Framework.Runtime;
#if DNX451 #if DNX451
using NLog.Config; using NLog.Config;
using NLog.Targets; using NLog.Targets;
@ -16,13 +17,13 @@ namespace IdentitySamples
{ {
public partial class Startup public partial class Startup
{ {
public Startup() public Startup(IApplicationEnvironment env)
{ {
/* /*
* Below code demonstrates usage of multiple configuration sources. For instance a setting say 'setting1' is found in both the registered sources, * Below code demonstrates usage of multiple configuration sources. For instance a setting say 'setting1' is found in both the registered sources,
* then the later source will win. By this way a Local config can be overridden by a different setting while deployed remotely. * then the later source will win. By this way a Local config can be overridden by a different setting while deployed remotely.
*/ */
Configuration = new Configuration() Configuration = new Configuration(env.ApplicationBasePath)
.AddJsonFile("LocalConfig.json") .AddJsonFile("LocalConfig.json")
.AddEnvironmentVariables(); //All environment variables in the process's context flow in as configuration values. .AddEnvironmentVariables(); //All environment variables in the process's context flow in as configuration values.
} }

View File

@ -5,6 +5,7 @@ using System;
using System.Security.Claims; using System.Security.Claims;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Authentication.Cookies; using Microsoft.AspNet.Authentication.Cookies;
using Microsoft.AspNet.Http.Authentication;
using Microsoft.Framework.DependencyInjection; using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.OptionsModel; using Microsoft.Framework.OptionsModel;
@ -24,12 +25,7 @@ namespace Microsoft.AspNet.Identity
var user = await manager.ValidateSecurityStampAsync(context.Principal, userId); var user = await manager.ValidateSecurityStampAsync(context.Principal, userId);
if (user != null) if (user != null)
{ {
var isPersistent = false; await manager.SignInAsync(user, context.Properties, authenticationMethod: null);
if (context.Properties != null)
{
isPersistent = context.Properties.IsPersistent;
}
await manager.SignInAsync(user, isPersistent, authenticationMethod: null);
} }
else else
{ {

View File

@ -32,7 +32,6 @@ namespace Microsoft.AspNet.Identity
{ {
throw new ArgumentNullException(nameof(userManager)); throw new ArgumentNullException(nameof(userManager));
} }
if (contextAccessor == null || contextAccessor.HttpContext == null) if (contextAccessor == null || contextAccessor.HttpContext == null)
{ {
throw new ArgumentNullException(nameof(contextAccessor)); throw new ArgumentNullException(nameof(contextAccessor));
@ -46,16 +45,14 @@ namespace Microsoft.AspNet.Identity
Context = contextAccessor.HttpContext; Context = contextAccessor.HttpContext;
ClaimsFactory = claimsFactory; ClaimsFactory = claimsFactory;
Options = optionsAccessor?.Options ?? new IdentityOptions(); Options = optionsAccessor?.Options ?? new IdentityOptions();
Logger = logger?.CreateLogger<SignInManager<TUser>>(); Logger = logger?.CreateLogger<SignInManager<TUser>>();
} }
protected internal virtual ILogger Logger { get; set; } protected internal virtual ILogger Logger { get; set; }
internal UserManager<TUser> UserManager { get; private set; } internal UserManager<TUser> UserManager { get; set; }
internal HttpContext Context { get; private set; } internal HttpContext Context { get; set; }
internal IUserClaimsPrincipalFactory<TUser> ClaimsFactory { get; private set; } internal IUserClaimsPrincipalFactory<TUser> ClaimsFactory { get; set; }
internal IdentityOptions Options { get; private set; } internal IdentityOptions Options { get; set; }
// Should this be a func? // Should this be a func?
public virtual async Task<ClaimsPrincipal> CreateUserPrincipalAsync(TUser user) => await ClaimsFactory.CreateAsync(user); public virtual async Task<ClaimsPrincipal> CreateUserPrincipalAsync(TUser user) => await ClaimsFactory.CreateAsync(user);
@ -74,7 +71,26 @@ namespace Microsoft.AspNet.Identity
return Logger.Log(true); return Logger.Log(true);
} }
public virtual async Task SignInAsync(TUser user, bool isPersistent, string authenticationMethod = null) /// <summary>
/// Called to regenerate the ApplicationCookie for the user, preserving the existing
/// AuthenticationProperties like rememberMe
/// </summary>
/// <param name="user"></param>
/// <returns></returns>
public virtual async Task RefreshSignInAsync(TUser user)
{
var authResult = await Context.AuthenticateAsync(IdentityOptions.ApplicationCookieAuthenticationScheme);
var properties = authResult?.Properties ?? new AuthenticationProperties();
var authenticationMethod = authResult?.Principal?.FindFirstValue(ClaimTypes.AuthenticationMethod);
await SignInAsync(user, properties, authenticationMethod);
}
public virtual Task SignInAsync(TUser user, bool isPersistent, string authenticationMethod = null)
{
return SignInAsync(user, new AuthenticationProperties { IsPersistent = isPersistent }, authenticationMethod);
}
public virtual async Task SignInAsync(TUser user, AuthenticationProperties authenticationProperties, string authenticationMethod = null)
{ {
var userPrincipal = await CreateUserPrincipalAsync(user); var userPrincipal = await CreateUserPrincipalAsync(user);
// Review: should we guard against CreateUserPrincipal returning null? // Review: should we guard against CreateUserPrincipal returning null?
@ -84,7 +100,7 @@ namespace Microsoft.AspNet.Identity
} }
Context.Response.SignIn(IdentityOptions.ApplicationCookieAuthenticationScheme, Context.Response.SignIn(IdentityOptions.ApplicationCookieAuthenticationScheme,
userPrincipal, userPrincipal,
new AuthenticationProperties() { IsPersistent = isPersistent }); authenticationProperties ?? new AuthenticationProperties());
} }
public virtual void SignOut() public virtual void SignOut()
@ -261,13 +277,12 @@ namespace Microsoft.AspNet.Identity
{ {
Context.Response.SignOut(IdentityOptions.ExternalCookieAuthenticationScheme); Context.Response.SignOut(IdentityOptions.ExternalCookieAuthenticationScheme);
} }
await SignInAsync(user, isPersistent, twoFactorInfo.LoginProvider);
if (rememberClient) if (rememberClient)
{ {
await RememberTwoFactorClientAsync(user); await RememberTwoFactorClientAsync(user);
} }
await UserManager.ResetAccessFailedCountAsync(user); await UserManager.ResetAccessFailedCountAsync(user);
await SignInAsync(user, isPersistent); await SignInAsync(user, isPersistent, twoFactorInfo.LoginProvider);
return Logger.Log(SignInResult.Success); return Logger.Log(SignInResult.Success);
} }
// If the token is incorrect, record the failure which also may cause the user to be locked out // If the token is incorrect, record the failure which also may cause the user to be locked out
@ -402,7 +417,6 @@ namespace Microsoft.AspNet.Identity
return Logger?.BeginScope(state); return Logger?.BeginScope(state);
} }
internal static ClaimsPrincipal StoreTwoFactorInfo(string userId, string loginProvider) internal static ClaimsPrincipal StoreTwoFactorInfo(string userId, string loginProvider)
{ {
var identity = new ClaimsIdentity(IdentityOptions.TwoFactorUserIdCookieAuthenticationType); var identity = new ClaimsIdentity(IdentityOptions.TwoFactorUserIdCookieAuthenticationType);

View File

@ -13,5 +13,8 @@
<PropertyGroup> <PropertyGroup>
<SchemaVersion>2.0</SchemaVersion> <SchemaVersion>2.0</SchemaVersion>
</PropertyGroup> </PropertyGroup>
<ItemGroup>
<Service Include="{82a7f48d-3b50-4b1e-b82e-3ada8210c358}" />
</ItemGroup>
<Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" /> <Import Project="$(VSToolsPath)\DNX\Microsoft.DNX.targets" Condition="'$(VSToolsPath)' != ''" />
</Project> </Project>

View File

@ -44,10 +44,11 @@ namespace Microsoft.AspNet.Identity.Test
var httpContext = new Mock<HttpContext>(); var httpContext = new Mock<HttpContext>();
var contextAccessor = new Mock<IHttpContextAccessor>(); var contextAccessor = new Mock<IHttpContextAccessor>();
contextAccessor.Setup(a => a.HttpContext).Returns(httpContext.Object); contextAccessor.Setup(a => a.HttpContext).Returns(httpContext.Object);
var properties = new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow, IsPersistent = isPersistent };
var signInManager = new Mock<SignInManager<TestUser>>(userManager.Object, var signInManager = new Mock<SignInManager<TestUser>>(userManager.Object,
contextAccessor.Object, claimsManager.Object, options.Object, null); contextAccessor.Object, claimsManager.Object, options.Object, null);
signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>(), user.Id)).ReturnsAsync(user).Verifiable(); signInManager.Setup(s => s.ValidateSecurityStampAsync(It.IsAny<ClaimsPrincipal>(), user.Id)).ReturnsAsync(user).Verifiable();
signInManager.Setup(s => s.SignInAsync(user, isPersistent, null)).Returns(Task.FromResult(0)).Verifiable(); signInManager.Setup(s => s.SignInAsync(user, properties, null)).Returns(Task.FromResult(0)).Verifiable();
var services = new ServiceCollection(); var services = new ServiceCollection();
services.AddInstance(options.Object); services.AddInstance(options.Object);
services.AddInstance(signInManager.Object); services.AddInstance(signInManager.Object);
@ -57,7 +58,7 @@ namespace Microsoft.AspNet.Identity.Test
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id)); id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
var ticket = new AuthenticationTicket(new ClaimsPrincipal(id), var ticket = new AuthenticationTicket(new ClaimsPrincipal(id),
new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow, IsPersistent = isPersistent }, properties,
IdentityOptions.ApplicationCookieAuthenticationScheme); IdentityOptions.ApplicationCookieAuthenticationScheme);
var context = new CookieValidatePrincipalContext(httpContext.Object, ticket, new CookieAuthenticationOptions()); var context = new CookieValidatePrincipalContext(httpContext.Object, ticket, new CookieAuthenticationOptions());
Assert.NotNull(context.Properties); Assert.NotNull(context.Properties);

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using System.Security.Claims; using System.Security.Claims;
using System.Security.Principal;
using System.Text; using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using Microsoft.AspNet.Hosting; using Microsoft.AspNet.Hosting;
@ -344,6 +343,42 @@ namespace Microsoft.AspNet.Identity.Test
response.Verify(); response.Verify();
} }
[Theory]
[InlineData(true, true)]
[InlineData(true, false)]
[InlineData(false, true)]
[InlineData(false, false)]
public async Task CanResignIn(bool isPersistent, bool externalLogin)
{
// Setup
var user = new TestUser { UserName = "Foo" };
var context = new Mock<HttpContext>();
var loginProvider = "loginprovider";
var id = new ClaimsIdentity();
if (externalLogin)
{
id.AddClaim(new Claim(ClaimTypes.AuthenticationMethod, loginProvider));
}
var properties = new AuthenticationProperties { IsPersistent = isPersistent };
var authResult = new AuthenticationResult(new ClaimsPrincipal(id), properties, new AuthenticationDescription());
context.Setup(c => c.AuthenticateAsync(IdentityOptions.ApplicationCookieAuthenticationScheme)).ReturnsAsync(authResult).Verifiable();
var manager = SetupUserManager(user);
var signInManager = new Mock<SignInManager<TestUser>>(manager.Object,
new HttpContextAccessor { HttpContext = context.Object },
new Mock<IUserClaimsPrincipalFactory<TestUser>>().Object,
null, null)
{ CallBase = true };
signInManager.Setup(s => s.SignInAsync(user, properties, externalLogin ? loginProvider : null)).Returns(Task.FromResult(0)).Verifiable();
signInManager.Object.Context = context.Object;
// Act
await signInManager.Object.RefreshSignInAsync(user);
// Assert
context.Verify();
signInManager.Verify();
}
[Theory] [Theory]
[InlineData(true, true, true, true)] [InlineData(true, true, true, true)]
[InlineData(true, true, false, true)] [InlineData(true, true, false, true)]