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

View File

@ -7,6 +7,7 @@ using Microsoft.Data.Entity;
using Microsoft.Framework.ConfigurationModel;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.Logging;
using Microsoft.Framework.Runtime;
#if DNX451
using NLog.Config;
using NLog.Targets;
@ -16,13 +17,13 @@ namespace IdentitySamples
{
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,
* 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")
.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.Threading.Tasks;
using Microsoft.AspNet.Authentication.Cookies;
using Microsoft.AspNet.Http.Authentication;
using Microsoft.Framework.DependencyInjection;
using Microsoft.Framework.OptionsModel;
@ -24,12 +25,7 @@ namespace Microsoft.AspNet.Identity
var user = await manager.ValidateSecurityStampAsync(context.Principal, userId);
if (user != null)
{
var isPersistent = false;
if (context.Properties != null)
{
isPersistent = context.Properties.IsPersistent;
}
await manager.SignInAsync(user, isPersistent, authenticationMethod: null);
await manager.SignInAsync(user, context.Properties, authenticationMethod: null);
}
else
{

View File

@ -32,7 +32,6 @@ namespace Microsoft.AspNet.Identity
{
throw new ArgumentNullException(nameof(userManager));
}
if (contextAccessor == null || contextAccessor.HttpContext == null)
{
throw new ArgumentNullException(nameof(contextAccessor));
@ -46,17 +45,15 @@ namespace Microsoft.AspNet.Identity
Context = contextAccessor.HttpContext;
ClaimsFactory = claimsFactory;
Options = optionsAccessor?.Options ?? new IdentityOptions();
Logger = logger?.CreateLogger<SignInManager<TUser>>();
}
protected internal virtual ILogger Logger { get; set; }
internal UserManager<TUser> UserManager { get; private set; }
internal HttpContext Context { get; private set; }
internal IUserClaimsPrincipalFactory<TUser> ClaimsFactory { get; private set; }
internal IdentityOptions Options { get; private set; }
internal UserManager<TUser> UserManager { get; set; }
internal HttpContext Context { get; set; }
internal IUserClaimsPrincipalFactory<TUser> ClaimsFactory { get; set; }
internal IdentityOptions Options { get; set; }
// Should this be a func?
public virtual async Task<ClaimsPrincipal> CreateUserPrincipalAsync(TUser user) => await ClaimsFactory.CreateAsync(user);
@ -74,7 +71,26 @@ namespace Microsoft.AspNet.Identity
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);
// Review: should we guard against CreateUserPrincipal returning null?
@ -84,7 +100,7 @@ namespace Microsoft.AspNet.Identity
}
Context.Response.SignIn(IdentityOptions.ApplicationCookieAuthenticationScheme,
userPrincipal,
new AuthenticationProperties() { IsPersistent = isPersistent });
authenticationProperties ?? new AuthenticationProperties());
}
public virtual void SignOut()
@ -261,13 +277,12 @@ namespace Microsoft.AspNet.Identity
{
Context.Response.SignOut(IdentityOptions.ExternalCookieAuthenticationScheme);
}
await SignInAsync(user, isPersistent, twoFactorInfo.LoginProvider);
if (rememberClient)
{
await RememberTwoFactorClientAsync(user);
}
await UserManager.ResetAccessFailedCountAsync(user);
await SignInAsync(user, isPersistent);
await SignInAsync(user, isPersistent, twoFactorInfo.LoginProvider);
return Logger.Log(SignInResult.Success);
}
// 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);
}
internal static ClaimsPrincipal StoreTwoFactorInfo(string userId, string loginProvider)
{
var identity = new ClaimsIdentity(IdentityOptions.TwoFactorUserIdCookieAuthenticationType);

View File

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

View File

@ -44,10 +44,11 @@ namespace Microsoft.AspNet.Identity.Test
var httpContext = new Mock<HttpContext>();
var contextAccessor = new Mock<IHttpContextAccessor>();
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,
contextAccessor.Object, claimsManager.Object, options.Object, null);
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();
services.AddInstance(options.Object);
services.AddInstance(signInManager.Object);
@ -57,7 +58,7 @@ namespace Microsoft.AspNet.Identity.Test
id.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Id));
var ticket = new AuthenticationTicket(new ClaimsPrincipal(id),
new AuthenticationProperties { IssuedUtc = DateTimeOffset.UtcNow, IsPersistent = isPersistent },
properties,
IdentityOptions.ApplicationCookieAuthenticationScheme);
var context = new CookieValidatePrincipalContext(httpContext.Object, ticket, new CookieAuthenticationOptions());
Assert.NotNull(context.Properties);

View File

@ -5,7 +5,6 @@ using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Security.Principal;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNet.Hosting;
@ -344,6 +343,42 @@ namespace Microsoft.AspNet.Identity.Test
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]
[InlineData(true, true, true, true)]
[InlineData(true, true, false, true)]