From 33d86451872217eb198a3214fca99444bfeef5f3 Mon Sep 17 00:00:00 2001 From: Hao Kung Date: Thu, 4 Dec 2014 13:25:18 -0800 Subject: [PATCH] Add ChangeEmail APIs --- .../IdentityOptions.cs | 2 + src/Microsoft.AspNet.Identity/UserManager.cs | 48 ++++++++++++- test/Shared/UserManagerTestBase.cs | 69 +++++++++++++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) diff --git a/src/Microsoft.AspNet.Identity/IdentityOptions.cs b/src/Microsoft.AspNet.Identity/IdentityOptions.cs index 0705a4dbd5..26e4092f38 100644 --- a/src/Microsoft.AspNet.Identity/IdentityOptions.cs +++ b/src/Microsoft.AspNet.Identity/IdentityOptions.cs @@ -29,6 +29,8 @@ namespace Microsoft.AspNet.Identity public string PasswordResetTokenProvider { get; set; } = Resources.DefaultTokenProvider; + public string ChangeEmailTokenProvider { get; set; } = Resources.DefaultTokenProvider; + public static string ApplicationCookieAuthenticationType { get; set; } = typeof(IdentityOptions).Namespace + ".Application"; public static string ExternalCookieAuthenticationType { get; set; } = typeof(IdentityOptions).Namespace + ".External"; public static string TwoFactorUserIdCookieAuthenticationType { get; set; } = typeof(IdentityOptions).Namespace + ".TwoFactorUserId"; diff --git a/src/Microsoft.AspNet.Identity/UserManager.cs b/src/Microsoft.AspNet.Identity/UserManager.cs index a42fa4c49f..3b18d534fd 100644 --- a/src/Microsoft.AspNet.Identity/UserManager.cs +++ b/src/Microsoft.AspNet.Identity/UserManager.cs @@ -716,7 +716,7 @@ namespace Microsoft.AspNet.Identity } /// - /// GenerateAsync a new security stamp for a user, used for SignOutEverywhere functionality + /// Generate a new security stamp for a user, used for SignOutEverywhere functionality /// /// /// @@ -1337,6 +1337,52 @@ namespace Microsoft.AspNet.Identity return await store.GetEmailConfirmedAsync(user, cancellationToken); } + private static string GetChangeEmailPurpose(string newEmail) + { + return "ChangeEmail:" + newEmail; + } + + /// + /// Generate a change email token for the user using the UserTokenProvider + /// + /// + /// + /// + public virtual async Task GenerateChangeEmailTokenAsync(TUser user, string newEmail, + CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + return await GenerateUserTokenAsync(user, Options.ChangeEmailTokenProvider, GetChangeEmailPurpose(newEmail), cancellationToken); + } + + /// + /// Change a user's email using a change email token + /// + /// + /// + /// + /// + /// + public virtual async Task ChangeEmailAsync(TUser user, string newEmail, string token, + CancellationToken cancellationToken = default(CancellationToken)) + { + ThrowIfDisposed(); + if (user == null) + { + throw new ArgumentNullException("user"); + } + // Make sure the token is valid and the stamp matches + if (!await VerifyUserTokenAsync(user, Options.ChangeEmailTokenProvider, GetChangeEmailPurpose(newEmail), token, cancellationToken)) + { + return IdentityResult.Failed(Resources.InvalidToken); + } + var store = GetEmailStore(); + await store.SetEmailAsync(user, newEmail, cancellationToken); + await store.SetEmailConfirmedAsync(user, true, cancellationToken); + await UpdateSecurityStampInternal(user, cancellationToken); + return await UpdateAsync(user, cancellationToken); + } + // IUserPhoneNumberStore methods internal IUserPhoneNumberStore GetPhoneNumberStore() { diff --git a/test/Shared/UserManagerTestBase.cs b/test/Shared/UserManagerTestBase.cs index 9f1fa064ce..2ba65a828a 100644 --- a/test/Shared/UserManagerTestBase.cs +++ b/test/Shared/UserManagerTestBase.cs @@ -1224,6 +1224,23 @@ namespace Microsoft.AspNet.Identity.Test Assert.Equal(stamp, user.SecurityStamp); } + [Fact] + public async Task ChangePhoneNumberFailsWithWrongPhoneNumber() + { + var manager = CreateManager(); + var user = CreateTestUser(); + user.PhoneNumber = "123-456-7890"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + var token1 = await manager.GenerateChangePhoneNumberTokenAsync(user, "111-111-1111"); + IdentityResultAssert.IsFailure(await manager.ChangePhoneNumberAsync(user, "bogus", token1), + "Invalid token."); + Assert.False(await manager.IsPhoneNumberConfirmedAsync(user)); + Assert.Equal(await manager.GetPhoneNumberAsync(user), "123-456-7890"); + Assert.Equal(stamp, user.SecurityStamp); + } + [Fact] public async Task CanVerifyPhoneNumber() { @@ -1241,6 +1258,58 @@ namespace Microsoft.AspNet.Identity.Test Assert.False(await manager.VerifyChangePhoneNumberTokenAsync(user, token1, num2)); } + [Fact] + public async Task CanChangeEmail() + { + var manager = CreateManager(); + var user = CreateTestUser(); + user.Email = user.UserName + "@diddly.bop"; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + string newEmail = user.UserName + "@en.vec"; + var token1 = await manager.GenerateChangeEmailTokenAsync(user, newEmail); + IdentityResultAssert.IsSuccess(await manager.ChangeEmailAsync(user, newEmail, token1)); + Assert.True(await manager.IsEmailConfirmedAsync(user)); + Assert.Equal(await manager.GetEmailAsync(user), newEmail); + Assert.NotEqual(stamp, user.SecurityStamp); + } + + [Fact] + public async Task ChangeEmailFailsWithWrongToken() + { + var manager = CreateManager(); + var user = CreateTestUser(); + user.Email = user.UserName + "@diddly.bop"; + string oldEmail = user.Email; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + IdentityResultAssert.IsFailure(await manager.ChangeEmailAsync(user, "whatevah@foo.barf", "bogus"), + "Invalid token."); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + Assert.Equal(await manager.GetEmailAsync(user), oldEmail); + Assert.Equal(stamp, user.SecurityStamp); + } + + [Fact] + public async Task ChangeEmailFailsWithEmail() + { + var manager = CreateManager(); + var user = CreateTestUser(); + user.Email = user.UserName + "@diddly.bop"; + string oldEmail = user.Email; + IdentityResultAssert.IsSuccess(await manager.CreateAsync(user)); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + var stamp = await manager.GetSecurityStampAsync(user); + var token1 = await manager.GenerateChangeEmailTokenAsync(user, "forgot@alrea.dy"); + IdentityResultAssert.IsFailure(await manager.ChangeEmailAsync(user, "oops@foo.barf", token1), + "Invalid token."); + Assert.False(await manager.IsEmailConfirmedAsync(user)); + Assert.Equal(await manager.GetEmailAsync(user), oldEmail); + Assert.Equal(stamp, user.SecurityStamp); + } + [Fact] public async Task CanEmailTwoFactorToken() {