Beispiel #1
0
        public async Task <Response <ChangeEmailViewModel, WebStatus> > CancelEmailAddressChangeAsync(string longCode)
        {
            _logger.LogTrace("Cancel email address change");

            var response = await _oneTimeCodeService.CheckOneTimeCodeAsync(longCode, null);

            if (response.Status.StatusCode != CheckOneTimeCodeStatusCode.VerifiedWithoutNonce) // TODO: investigate if VerifiedWithNonce is possible and valid
            {
                return(Response.Web.Error <ChangeEmailViewModel>(_localizer["Invalid code."], HttpStatusCode.BadRequest));
            }
            var userResponse = await _userStore.GetUserByPreviousEmailAsync(response.Result.SentTo);

            if (userResponse.HasError)
            {
                return(Response.Web.Error <ChangeEmailViewModel>(_localizer["User not found."], HttpStatusCode.BadRequest));
            }
            var user = userResponse.Result;

            var changes = new Dictionary <string, string>
            {
                ["email"] = response.Result.SentTo,
                [PasswordlessLoginConstants.Security.PreviousEmailClaimType] = null
            };
            var updateUserResponse = await _userStore.PatchUserAsync(user.SubjectId, changes.ToLookup(x => x.Key, x => x.Value), true);

            if (updateUserResponse.HasError)
            {
                var patchStatus = new WebStatus(updateUserResponse.Status);
                patchStatus.StatusCode = HttpStatusCode.BadRequest;
                return(new Response <ChangeEmailViewModel, WebStatus>(patchStatus));
            }
            var updatedUser = updateUserResponse.Result;
            var viewModel   = new ChangeEmailViewModel()
            {
                OldEmail = user.Email,
                NewEmail = updatedUser.Email,
            };
            await _eventNotificationService.NotifyEventAsync(viewModel.OldEmail, EventType.CancelEmailChange, $"Reverted to {viewModel.NewEmail}");

            return(Response.Web.Success(viewModel, _localizer["Email address change has been reverted."]));
        }
Beispiel #2
0
        public async Task <Response <ChangeEmailViewModel, WebStatus> > ChangeEmailAddressAsync(
            string newEmail, string applicationId = null)
        {
            _logger.LogTrace("Change email for user {0} to {1}", _httpContext.User.GetSubjectId(), newEmail);

            var userResponse = await GetUserAsync();

            if (userResponse.HasError)
            {
                return(Response.Web.Error <ChangeEmailViewModel>("User not found.", HttpStatusCode.NotFound));
            }
            var user = userResponse.Result;

            // Check if a cancel email change code link is still valid. Note that the cancel email change code link does
            // not interfere with getting a one time code to sign in because it is associated with the OLD email address.
            var previouslyChangedEmail = user.Claims.FirstOrDefault(x => x.Type == PasswordlessLoginConstants.Security.PreviousEmailClaimType)?.Value;

            if (previouslyChangedEmail != null && (await _oneTimeCodeService.UnexpiredOneTimeCodeExistsAsync(previouslyChangedEmail)))
            {
                return(Response.Web.Error <ChangeEmailViewModel>(_localizer["You cannot change your email address again until the link to cancel the last email change (sent to your old email address) expires."], HttpStatusCode.Forbidden));
            }
            var usernameAvailableStatus = await UsernameIsReallyAvailableAsync(newEmail, user.SubjectId);

            if (usernameAvailableStatus.HasError)
            {
                return(Response.Web.Error <ChangeEmailViewModel>(_localizer["Username is not available."], HttpStatusCode.Conflict));
            }
            var oldEmail = user.Email;

            if (_passwordlessLoginOptions.SendCancelEmailChangeMessage)
            {
                var otcResponse = await _oneTimeCodeService.GetOneTimeCodeAsync(oldEmail, TimeSpan.FromHours(_passwordlessLoginOptions.CancelEmailChangeTimeWindowHours));

                var status = await _messageService.SendEmailChangedNoticeAsync(applicationId, oldEmail, otcResponse.Result.LongCode);

                if (status.HasError)
                {
                    // TODO: review to ensure that this will not prevent changing one's email address if the
                    // old address is undeliverable
                    return(Response.Web.Error <ChangeEmailViewModel>($"{_localizer["Change cancelled because of failure to send email notice:"]} {status.Text}"));
                }
            }

            var changes = new Dictionary <string, string>
            {
                ["email"] = newEmail,
                [PasswordlessLoginConstants.Security.PreviousEmailClaimType] = oldEmail
            };
            var updateUserResponse = await _userStore.PatchUserAsync(user.SubjectId, changes.ToLookup(x => x.Key, x => x.Value), true);

            if (updateUserResponse.HasError)
            {
                var patchStatus = new WebStatus(updateUserResponse.Status);
                patchStatus.StatusCode = HttpStatusCode.BadRequest;
                return(new Response <ChangeEmailViewModel, WebStatus>(patchStatus));
            }
            var updatedUser = updateUserResponse.Result;
            var viewModel   = new ChangeEmailViewModel()
            {
                OldEmail = user.Email,
                NewEmail = updatedUser.Email,
            };
            await _eventNotificationService.NotifyEventAsync(viewModel.OldEmail, EventType.EmailChange, $"Changed to {viewModel.NewEmail}");

            return(Response.Web.Success(viewModel));
        }