Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/Identity/Core/src/SignInManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,13 @@ public virtual async Task RefreshSignInAsync(TUser user)
return (false, auth.Properties?.IsPersistent);
}

var presentedStamp = auth.Principal?.FindFirstValue(Options.ClaimsIdentity.SecurityStampClaimType);
if (!await ValidateSecurityStampAsync(user, presentedStamp))
{
Logger.LogError("RefreshSignInAsync prevented because the presented security stamp is stale.");
return (false, auth.Properties?.IsPersistent);
}

IList<Claim> claims = Array.Empty<Claim>();
var authenticationMethod = auth.Principal?.FindFirst(ClaimTypes.AuthenticationMethod);
var amr = auth.Principal?.FindFirst("amr");
Expand Down
62 changes: 62 additions & 0 deletions src/Identity/test/Identity.Test/SignInManagerTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1091,6 +1091,68 @@ public async Task ResignInNoOpsAndLogsErrorIfAuthenticatedWithDifferentUser()
Times.Never());
}

[Fact]
public async Task ResignInNoOpsAndLogsErrorIfSecurityStampIsStale()
{
var user = new PocoUser { UserName = "Foo" };
var context = new DefaultHttpContext();
var auth = MockAuth(context);
var manager = SetupUserManager(user);
manager.Setup(m => m.SupportsUserSecurityStamp).Returns(true);
manager.Setup(m => m.GetSecurityStampAsync(user)).ReturnsAsync("current-stamp");
var logger = new TestLogger<SignInManager<PocoUser>>();
var signInManager = new Mock<SignInManager<PocoUser>>(manager.Object,
new HttpContextAccessor { HttpContext = context },
new Mock<IUserClaimsPrincipalFactory<PocoUser>>().Object,
null, logger, new Mock<IAuthenticationSchemeProvider>().Object, null)
{ CallBase = true };
var id = new ClaimsIdentity("authscheme");
id.AddClaim(new Claim(new IdentityOptions().ClaimsIdentity.SecurityStampClaimType, "stale-stamp"));
var claimsPrincipal = new ClaimsPrincipal(id);
var authResult = AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, new AuthenticationProperties(), "authscheme"));
auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.ApplicationScheme))
.Returns(Task.FromResult(authResult)).Verifiable();
manager.Setup(m => m.GetUserId(claimsPrincipal)).Returns(user.Id.ToString());

await signInManager.Object.RefreshSignInAsync(user);

Assert.Contains("RefreshSignInAsync prevented because the presented security stamp is stale.", logger.LogMessages);
auth.Verify();
signInManager.Verify(s => s.SignInWithClaimsAsync(It.IsAny<PocoUser>(), It.IsAny<AuthenticationProperties>(), It.IsAny<IEnumerable<Claim>>()),
Times.Never());
}

[Fact]
public async Task CanResignInWhenSecurityStampMatches()
{
var user = new PocoUser { UserName = "Foo" };
var context = new DefaultHttpContext();
var auth = MockAuth(context);
var manager = SetupUserManager(user);
manager.Setup(m => m.SupportsUserSecurityStamp).Returns(true);
manager.Setup(m => m.GetSecurityStampAsync(user)).ReturnsAsync("current-stamp");
var id = new ClaimsIdentity("authscheme");
id.AddClaim(new Claim(new IdentityOptions().ClaimsIdentity.SecurityStampClaimType, "current-stamp"));
var claimsPrincipal = new ClaimsPrincipal(id);
var authResult = AuthenticateResult.Success(new AuthenticationTicket(claimsPrincipal, new AuthenticationProperties(), "authscheme"));
auth.Setup(a => a.AuthenticateAsync(context, IdentityConstants.ApplicationScheme))
.Returns(Task.FromResult(authResult)).Verifiable();
manager.Setup(m => m.GetUserId(claimsPrincipal)).Returns(user.Id.ToString());
var signInManager = new Mock<SignInManager<PocoUser>>(manager.Object,
new HttpContextAccessor { HttpContext = context },
new Mock<IUserClaimsPrincipalFactory<PocoUser>>().Object,
null, null, new Mock<IAuthenticationSchemeProvider>().Object, null)
{ CallBase = true };
signInManager.Setup(s => s.SignInWithClaimsAsync(user, It.IsAny<AuthenticationProperties>(), It.IsAny<IEnumerable<Claim>>()))
.Returns(Task.FromResult(0)).Verifiable();
signInManager.Object.Context = context;

await signInManager.Object.RefreshSignInAsync(user);

auth.Verify();
signInManager.Verify();
}

[Theory]
[InlineData(true, true, true, true)]
[InlineData(true, true, false, true)]
Expand Down
Loading