Skip to content
This repository was archived by the owner on Dec 20, 2018. It is now read-only.

Commit 8c47b90

Browse files
committed
Update fix to ChangePhoneNumber
- Added Quirk mode to control default provider - Fixed new regression
1 parent 98fa107 commit 8c47b90

File tree

4 files changed

+60
-18
lines changed

4 files changed

+60
-18
lines changed

src/Microsoft.AspNetCore.Identity.Specification.Tests/UserManagerSpecificationTests.cs

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1568,12 +1568,48 @@ public async Task ChangePhoneNumberFailsWithWrongToken()
15681568
var stamp = await manager.GetSecurityStampAsync(user);
15691569
IdentityResultAssert.IsFailure(await manager.ChangePhoneNumberAsync(user, "111-111-1111", "bogus"),
15701570
"Invalid token.");
1571-
IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyChangePhoneNumberTokenAsync() failed for user { await manager.GetUserIdAsync(user)}.");
1571+
IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangePhoneNumber:111-111-1111 for user {await manager.GetUserIdAsync(user)}.");
15721572
Assert.False(await manager.IsPhoneNumberConfirmedAsync(user));
15731573
Assert.Equal("123-456-7890", await manager.GetPhoneNumberAsync(user));
15741574
Assert.Equal(stamp, await manager.GetSecurityStampAsync(user));
15751575
}
15761576

1577+
private class YesPhoneNumberProvider : IUserTwoFactorTokenProvider<TUser>
1578+
{
1579+
public Task<bool> CanGenerateTwoFactorTokenAsync(UserManager<TUser> manager, TUser user)
1580+
=> Task.FromResult(true);
1581+
1582+
public Task<string> GenerateAsync(string purpose, UserManager<TUser> manager, TUser user)
1583+
=> Task.FromResult(purpose);
1584+
1585+
public Task<bool> ValidateAsync(string purpose, string token, UserManager<TUser> manager, TUser user)
1586+
=> Task.FromResult(true);
1587+
}
1588+
1589+
/// <summary>
1590+
/// Test.
1591+
/// </summary>
1592+
/// <returns>Task</returns>
1593+
[Fact]
1594+
public async Task ChangePhoneNumberWithCustomProvider()
1595+
{
1596+
if (ShouldSkipDbTests())
1597+
{
1598+
return;
1599+
}
1600+
var manager = CreateManager();
1601+
manager.RegisterTokenProvider("Yes", new YesPhoneNumberProvider());
1602+
manager.Options.Tokens.ChangePhoneNumberTokenProvider = "Yes";
1603+
var user = CreateTestUser(phoneNumber: "123-456-7890");
1604+
IdentityResultAssert.IsSuccess(await manager.CreateAsync(user));
1605+
Assert.False(await manager.IsPhoneNumberConfirmedAsync(user));
1606+
var stamp = await manager.GetSecurityStampAsync(user);
1607+
IdentityResultAssert.IsSuccess(await manager.ChangePhoneNumberAsync(user, "111-111-1111", "whatever"));
1608+
Assert.True(await manager.IsPhoneNumberConfirmedAsync(user));
1609+
Assert.Equal("111-111-1111", await manager.GetPhoneNumberAsync(user));
1610+
Assert.NotEqual(stamp, await manager.GetSecurityStampAsync(user));
1611+
}
1612+
15771613
/// <summary>
15781614
/// Test.
15791615
/// </summary>
@@ -1623,7 +1659,8 @@ public async Task CanVerifyPhoneNumber()
16231659
Assert.True(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num2));
16241660
Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token2, num1));
16251661
Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num2));
1626-
IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyChangePhoneNumberTokenAsync() failed for user {await manager.GetUserIdAsync(user)}.");
1662+
IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangePhoneNumber:{num1} for user {await manager.GetUserIdAsync(user)}.");
1663+
IdentityResultAssert.VerifyLogMessage(manager.Logger, $"VerifyUserTokenAsync() failed with purpose: ChangePhoneNumber:{num2} for user {await manager.GetUserIdAsync(user)}.");
16271664
}
16281665

16291666
/// <summary>

src/Microsoft.Extensions.Identity.Core/TokenOptions.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ public class TokenOptions
6666
/// <value>
6767
/// The <see cref="ChangePhoneNumberTokenProvider"/> used to generate tokens used when changing phone numbers.
6868
/// </value>
69-
public string ChangePhoneNumberTokenProvider { get; set; } = DefaultProvider;
69+
public string ChangePhoneNumberTokenProvider { get; set; } = GetDefaultChangePhoneNumberTokenProvider();
7070

7171
/// <summary>
7272
/// Gets or sets the <see cref="AuthenticatorTokenProvider"/> used to validate two factor sign ins with an authenticator.
@@ -75,5 +75,9 @@ public class TokenOptions
7575
/// The <see cref="AuthenticatorTokenProvider"/> used to validate two factor sign ins with an authenticator.
7676
/// </value>
7777
public string AuthenticatorTokenProvider { get; set; } = DefaultAuthenticatorProvider;
78+
79+
private static string GetDefaultChangePhoneNumberTokenProvider()
80+
=> (AppContext.TryGetSwitch("Microsoft.AspNetCore.Identity.ChangePhoneNumberTokenProvider1483", out var enabled) && enabled)
81+
? DefaultProvider : DefaultPhoneProvider;
7882
}
7983
}

src/Microsoft.Extensions.Identity.Core/UserManager.cs

Lines changed: 8 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1580,12 +1580,10 @@ public virtual Task<bool> IsPhoneNumberConfirmedAsync(TUser user)
15801580
/// <returns>
15811581
/// The <see cref="Task"/> that represents the asynchronous operation, containing the telephone change number token.
15821582
/// </returns>
1583-
public virtual async Task<string> GenerateChangePhoneNumberTokenAsync(TUser user, string phoneNumber)
1583+
public virtual Task<string> GenerateChangePhoneNumberTokenAsync(TUser user, string phoneNumber)
15841584
{
15851585
ThrowIfDisposed();
1586-
return Rfc6238AuthenticationService.GenerateCode(
1587-
await CreateSecurityTokenAsync(user), phoneNumber)
1588-
.ToString(CultureInfo.InvariantCulture);
1586+
return GenerateUserTokenAsync(user, Options.Tokens.ChangePhoneNumberTokenProvider, ChangePhoneNumberTokenPurpose + ":" + phoneNumber);
15891587
}
15901588

15911589
/// <summary>
@@ -1599,21 +1597,16 @@ await CreateSecurityTokenAsync(user), phoneNumber)
15991597
/// The <see cref="Task"/> that represents the asynchronous operation, returning true if the <paramref name="token"/>
16001598
/// is valid, otherwise false.
16011599
/// </returns>
1602-
public virtual async Task<bool> VerifyChangePhoneNumberTokenAsync(TUser user, string token, string phoneNumber)
1600+
public virtual Task<bool> VerifyChangePhoneNumberTokenAsync(TUser user, string token, string phoneNumber)
16031601
{
16041602
ThrowIfDisposed();
1605-
1606-
var securityToken = await CreateSecurityTokenAsync(user);
1607-
int code;
1608-
if (securityToken != null && Int32.TryParse(token, out code))
1603+
if (user == null)
16091604
{
1610-
if (Rfc6238AuthenticationService.ValidateCode(securityToken, code, phoneNumber))
1611-
{
1612-
return true;
1613-
}
1605+
throw new ArgumentNullException(nameof(user));
16141606
}
1615-
Logger.LogWarning(8, "VerifyChangePhoneNumberTokenAsync() failed for user {userId}.", await GetUserIdAsync(user));
1616-
return false;
1607+
1608+
// Make sure the token is valid and the stamp matches
1609+
return VerifyUserTokenAsync(user, Options.Tokens.ChangePhoneNumberTokenProvider, ChangePhoneNumberTokenPurpose+":"+ phoneNumber, token);
16171610
}
16181611

16191612
/// <summary>

test/Microsoft.AspNetCore.Identity.Test/IdentityOptionsTest.cs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ namespace Microsoft.AspNetCore.Identity.Test
1414
{
1515
public class IdentityOptionsTest
1616
{
17+
[Fact]
18+
public void VerifyQuirkChangePhoneNumberTokenProvider1483()
19+
{
20+
Assert.Equal(TokenOptions.DefaultPhoneProvider, new TokenOptions().ChangePhoneNumberTokenProvider);
21+
AppContext.SetSwitch("Microsoft.AspNetCore.Identity.ChangePhoneNumberTokenProvider1483", true);
22+
Assert.Equal(TokenOptions.DefaultProvider, new TokenOptions().ChangePhoneNumberTokenProvider);
23+
}
24+
1725
[Fact]
1826
public void VerifyDefaultOptions()
1927
{

0 commit comments

Comments
 (0)