Ask for a new password when recovering by email.

This commit is contained in:
John Preston 2021-07-30 17:33:26 +03:00
parent 5a882d1fdc
commit c3595f2e31
9 changed files with 256 additions and 143 deletions

View file

@ -189,21 +189,33 @@ PasscodeBox::PasscodeBox(
PasscodeBox::PasscodeBox( PasscodeBox::PasscodeBox(
QWidget*, QWidget*,
not_null<Main::Session*> session, not_null<MTP::Instance*> mtp,
Main::Session *session,
const CloudFields &fields) const CloudFields &fields)
: _session(session) : _session(session)
, _api(&_session->mtp()) , _api(mtp)
, _turningOff(fields.turningOff) , _turningOff(fields.turningOff)
, _cloudPwd(true) , _cloudPwd(true)
, _cloudFields(fields) , _cloudFields(fields)
, _about(st::boxWidth - st::boxPadding.left() * 1.5) , _about(st::boxWidth - st::boxPadding.left() * 1.5)
, _oldPasscode(this, st::defaultInputField, tr::lng_cloud_password_enter_old()) , _oldPasscode(this, st::defaultInputField, tr::lng_cloud_password_enter_old())
, _newPasscode(this, st::defaultInputField, fields.curRequest ? tr::lng_cloud_password_enter_new() : tr::lng_cloud_password_enter_first()) , _newPasscode(
this,
st::defaultInputField,
(fields.curRequest
? tr::lng_cloud_password_enter_new()
: tr::lng_cloud_password_enter_first()))
, _reenterPasscode(this, st::defaultInputField, tr::lng_cloud_password_confirm_new()) , _reenterPasscode(this, st::defaultInputField, tr::lng_cloud_password_confirm_new())
, _passwordHint(this, st::defaultInputField, fields.curRequest ? tr::lng_cloud_password_change_hint() : tr::lng_cloud_password_hint()) , _passwordHint(
this,
st::defaultInputField,
(fields.curRequest
? tr::lng_cloud_password_change_hint()
: tr::lng_cloud_password_hint()))
, _recoverEmail(this, st::defaultInputField, tr::lng_cloud_password_email()) , _recoverEmail(this, st::defaultInputField, tr::lng_cloud_password_email())
, _recover(this, tr::lng_signin_recover(tr::now)) , _recover(this, tr::lng_signin_recover(tr::now))
, _showRecoverLink(_cloudFields.hasRecovery || !_cloudFields.pendingResetDate) { , _showRecoverLink(_cloudFields.hasRecovery || !_cloudFields.pendingResetDate) {
Expects(session != nullptr || !fields.fromRecoveryCode.isEmpty());
Expects(!_turningOff || _cloudFields.curRequest); Expects(!_turningOff || _cloudFields.curRequest);
if (!_cloudFields.hint.isEmpty()) { if (!_cloudFields.hint.isEmpty()) {
@ -213,6 +225,13 @@ PasscodeBox::PasscodeBox(
} }
} }
PasscodeBox::PasscodeBox(
QWidget*,
not_null<Main::Session*> session,
const CloudFields &fields)
: PasscodeBox(nullptr, &session->mtp(), session, fields) {
}
rpl::producer<QByteArray> PasscodeBox::newPasswordSet() const { rpl::producer<QByteArray> PasscodeBox::newPasswordSet() const {
return _newPasswordSet.events(); return _newPasswordSet.events();
} }
@ -225,6 +244,10 @@ rpl::producer<> PasscodeBox::clearUnconfirmedPassword() const {
return _clearUnconfirmedPassword.events(); return _clearUnconfirmedPassword.events();
} }
rpl::producer<MTPauth_Authorization> PasscodeBox::newAuthorization() const {
return _newAuthorization.events();
}
bool PasscodeBox::currentlyHave() const { bool PasscodeBox::currentlyHave() const {
return _cloudPwd return _cloudPwd
? (!!_cloudFields.curRequest) ? (!!_cloudFields.curRequest)
@ -272,9 +295,11 @@ void PasscodeBox::prepare() {
} else { } else {
_oldPasscode->hide(); _oldPasscode->hide();
setTitle(_cloudPwd setTitle(_cloudPwd
? tr::lng_cloud_password_create() ? (_cloudFields.fromRecoveryCode.isEmpty()
? tr::lng_cloud_password_create()
: tr::lng_cloud_password_change())
: tr::lng_passcode_create()); : tr::lng_passcode_create());
setDimensions(st::boxWidth, st::passcodePadding.top() + _newPasscode->height() + st::passcodeLittleSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::passcodeLittleSkip : 0) + st::passcodeAboutSkip + _aboutHeight + (_cloudPwd ? (st::passcodeLittleSkip + _recoverEmail->height() + st::passcodeSkip) : st::passcodePadding.bottom())); setDimensions(st::boxWidth, st::passcodePadding.top() + _newPasscode->height() + st::passcodeLittleSkip + _reenterPasscode->height() + st::passcodeSkip + (_cloudPwd ? _passwordHint->height() + st::passcodeLittleSkip : 0) + st::passcodeAboutSkip + _aboutHeight + ((_cloudPwd && _cloudFields.fromRecoveryCode.isEmpty()) ? (st::passcodeLittleSkip + _recoverEmail->height() + st::passcodeSkip) : st::passcodePadding.bottom()));
} }
} }
@ -301,7 +326,10 @@ void PasscodeBox::prepare() {
_newPasscode->setVisible(!onlyCheck); _newPasscode->setVisible(!onlyCheck);
_reenterPasscode->setVisible(!onlyCheck); _reenterPasscode->setVisible(!onlyCheck);
_passwordHint->setVisible(!onlyCheck && _cloudPwd); _passwordHint->setVisible(!onlyCheck && _cloudPwd);
_recoverEmail->setVisible(!onlyCheck && _cloudPwd && !has); _recoverEmail->setVisible(!onlyCheck
&& _cloudPwd
&& !has
&& _cloudFields.fromRecoveryCode.isEmpty());
} }
void PasscodeBox::submit() { void PasscodeBox::submit() {
@ -400,21 +428,42 @@ void PasscodeBox::setInnerFocus() {
} }
} }
void PasscodeBox::recoverPasswordDone(
const QByteArray &newPasswordBytes,
const MTPauth_Authorization &result) {
if (_replacedBy) {
_replacedBy->closeBox();
}
_setRequest = 0;
const auto weak = Ui::MakeWeak(this);
_newAuthorization.fire_copy(result);
_newPasswordSet.fire_copy(newPasswordBytes);
if (weak) {
getDelegate()->show(Box<InformBox>(
tr::lng_cloud_password_updated(tr::now)));
if (weak) {
closeBox();
}
}
}
void PasscodeBox::setPasswordDone(const QByteArray &newPasswordBytes) { void PasscodeBox::setPasswordDone(const QByteArray &newPasswordBytes) {
if (_replacedBy) { if (_replacedBy) {
_replacedBy->closeBox(); _replacedBy->closeBox();
} }
_setRequest = 0; _setRequest = 0;
_newPasswordSet.fire_copy(newPasswordBytes);
const auto weak = Ui::MakeWeak(this); const auto weak = Ui::MakeWeak(this);
const auto text = _reenterPasscode->isHidden() _newPasswordSet.fire_copy(newPasswordBytes);
? tr::lng_cloud_password_removed(tr::now)
: _oldPasscode->isHidden()
? tr::lng_cloud_password_was_set(tr::now)
: tr::lng_cloud_password_updated(tr::now);
getDelegate()->show(Box<InformBox>(text));
if (weak) { if (weak) {
closeBox(); const auto text = _reenterPasscode->isHidden()
? tr::lng_cloud_password_removed(tr::now)
: _oldPasscode->isHidden()
? tr::lng_cloud_password_was_set(tr::now)
: tr::lng_cloud_password_updated(tr::now);
getDelegate()->show(Box<InformBox>(text));
if (weak) {
closeBox();
}
} }
} }
@ -799,25 +848,40 @@ void PasscodeBox::setNewCloudPassword(const QString &newPassword) {
} }
const auto hint = _passwordHint->getLastText(); const auto hint = _passwordHint->getLastText();
const auto email = _recoverEmail->getLastText().trimmed(); const auto email = _recoverEmail->getLastText().trimmed();
const auto flags = MTPDaccount_passwordInputSettings::Flag::f_new_algo using Flag = MTPDaccount_passwordInputSettings::Flag;
| MTPDaccount_passwordInputSettings::Flag::f_new_password_hash const auto flags = Flag::f_new_algo
| MTPDaccount_passwordInputSettings::Flag::f_hint | Flag::f_new_password_hash
| MTPDaccount_passwordInputSettings::Flag::f_email; | Flag::f_hint
| (_cloudFields.fromRecoveryCode.isEmpty() ? Flag::f_email : Flag(0));
_checkPasswordCallback = nullptr; _checkPasswordCallback = nullptr;
_setRequest = _api.request(MTPaccount_UpdatePasswordSettings(
MTP_inputCheckPasswordEmpty(), const auto settings = MTP_account_passwordInputSettings(
MTP_account_passwordInputSettings( MTP_flags(flags),
MTP_flags(flags), Core::PrepareCloudPasswordAlgo(_cloudFields.newAlgo),
Core::PrepareCloudPasswordAlgo(_cloudFields.newAlgo), MTP_bytes(newPasswordHash.modpow),
MTP_bytes(newPasswordHash.modpow), MTP_string(hint),
MTP_string(hint), MTP_string(email),
MTP_string(email), MTPSecureSecretSettings());
MTPSecureSecretSettings()) if (_cloudFields.fromRecoveryCode.isEmpty()) {
)).done([=](const MTPBool &result) { _setRequest = _api.request(MTPaccount_UpdatePasswordSettings(
setPasswordDone(newPasswordBytes); MTP_inputCheckPasswordEmpty(),
}).fail([=](const MTP::Error &error) { settings
setPasswordFail(newPasswordBytes, email, error); )).done([=](const MTPBool &result) {
}).handleFloodErrors().send(); setPasswordDone(newPasswordBytes);
}).fail([=](const MTP::Error &error) {
setPasswordFail(newPasswordBytes, email, error);
}).handleFloodErrors().send();
} else {
_setRequest = _api.request(MTPauth_RecoverPassword(
MTP_flags(MTPauth_RecoverPassword::Flag::f_new_settings),
MTP_string(_cloudFields.fromRecoveryCode),
settings
)).done([=](const MTPauth_Authorization &result) {
}).fail([=](const MTP::Error &error) {
setPasswordFail(newPasswordBytes, email, error);
}).handleFloodErrors().send();
}
} }
void PasscodeBox::changeCloudPassword( void PasscodeBox::changeCloudPassword(
@ -1005,6 +1069,7 @@ void PasscodeBox::emailChanged() {
void PasscodeBox::recoverByEmail() { void PasscodeBox::recoverByEmail() {
if (!_cloudFields.hasRecovery) { if (!_cloudFields.hasRecovery) {
Assert(_session != nullptr);
const auto session = _session; const auto session = _session;
const auto confirmBox = std::make_shared<QPointer<BoxContent>>(); const auto confirmBox = std::make_shared<QPointer<BoxContent>>();
const auto reset = crl::guard(this, [=] { const auto reset = crl::guard(this, [=] {
@ -1036,19 +1101,19 @@ void PasscodeBox::recoverExpired() {
} }
void PasscodeBox::recover() { void PasscodeBox::recover() {
if (_pattern == "-") return; if (_pattern == "-" || !_session) {
return;
}
const auto weak = Ui::MakeWeak(this); const auto weak = Ui::MakeWeak(this);
const auto box = getDelegate()->show(Box<RecoverBox>( const auto box = getDelegate()->show(Box<RecoverBox>(
&_api.instance(),
_session, _session,
_pattern, _pattern,
_cloudFields.notEmptyPassport, _cloudFields,
_cloudFields.pendingResetDate != 0,
[weak] { if (weak) { weak->closeBox(); } })); [weak] { if (weak) { weak->closeBox(); } }));
box->passwordCleared( box->newPasswordSet(
) | rpl::map_to(
QByteArray()
) | rpl::start_to_stream(_newPasswordSet, lifetime()); ) | rpl::start_to_stream(_newPasswordSet, lifetime());
box->recoveryExpired( box->recoveryExpired(
@ -1071,18 +1136,19 @@ void PasscodeBox::recoverStartFail(const MTP::Error &error) {
RecoverBox::RecoverBox( RecoverBox::RecoverBox(
QWidget*, QWidget*,
not_null<Main::Session*> session, not_null<MTP::Instance*> mtp,
Main::Session *session,
const QString &pattern, const QString &pattern,
bool notEmptyPassport, const PasscodeBox::CloudFields &fields,
bool hasPendingReset,
Fn<void()> closeParent) Fn<void()> closeParent)
: _api(&session->mtp()) : _session(session)
, _api(mtp)
, _pattern(st::normalFont->elided(tr::lng_signin_recover_hint(tr::now, lt_recover_email, pattern), st::boxWidth - st::boxPadding.left() * 1.5)) , _pattern(st::normalFont->elided(tr::lng_signin_recover_hint(tr::now, lt_recover_email, pattern), st::boxWidth - st::boxPadding.left() * 1.5))
, _notEmptyPassport(notEmptyPassport) , _cloudFields(fields)
, _recoverCode(this, st::defaultInputField, tr::lng_signin_code()) , _recoverCode(this, st::defaultInputField, tr::lng_signin_code())
, _noEmailAccess(this, tr::lng_signin_try_password(tr::now)) , _noEmailAccess(this, tr::lng_signin_try_password(tr::now))
, _closeParent(std::move(closeParent)) { , _closeParent(std::move(closeParent)) {
if (hasPendingReset) { if (_cloudFields.pendingResetDate != 0 || !session) {
_noEmailAccess.destroy(); _noEmailAccess.destroy();
} else { } else {
_noEmailAccess->setClickedCallback([=] { _noEmailAccess->setClickedCallback([=] {
@ -1106,8 +1172,8 @@ RecoverBox::RecoverBox(
} }
} }
rpl::producer<> RecoverBox::passwordCleared() const { rpl::producer<QByteArray> RecoverBox::newPasswordSet() const {
return _passwordCleared.events(); return _newPasswordSet.events();
} }
rpl::producer<> RecoverBox::recoveryExpired() const { rpl::producer<> RecoverBox::recoveryExpired() const {
@ -1173,17 +1239,15 @@ void RecoverBox::submit() {
} }
const auto send = crl::guard(this, [=] { const auto send = crl::guard(this, [=] {
_submitRequest = _api.request(MTPauth_RecoverPassword( _submitRequest = _api.request(MTPauth_CheckRecoveryPassword(
MTP_flags(0), MTP_string(code)
MTP_string(code), )).done([=](const MTPBool &result) {
MTPaccount_PasswordInputSettings() checkSubmitDone(code, result);
)).done([=](const MTPauth_Authorization &result) {
codeSubmitDone(result);
}).fail([=](const MTP::Error &error) { }).fail([=](const MTP::Error &error) {
codeSubmitFail(error); checkSubmitFail(error);
}).handleFloodErrors().send(); }).handleFloodErrors().send();
}); });
if (_notEmptyPassport) { if (_cloudFields.notEmptyPassport) {
const auto confirmed = [=](Fn<void()> &&close) { const auto confirmed = [=](Fn<void()> &&close) {
send(); send();
close(); close();
@ -1209,16 +1273,38 @@ void RecoverBox::codeChanged() {
setError(QString()); setError(QString());
} }
void RecoverBox::codeSubmitDone(const MTPauth_Authorization &result) { void RecoverBox::checkSubmitDone(const QString &code, const MTPBool &result) {
_submitRequest = 0; _submitRequest = 0;
_passwordCleared.fire({}); auto fields = _cloudFields;
getDelegate()->show( fields.fromRecoveryCode = code;
Box<InformBox>(tr::lng_cloud_password_removed(tr::now)), fields.hasRecovery = false;
Ui::LayerOption::CloseOther); // we could've been turning off, no need to force new password then
// like if (_cloudFields.turningOff) { just RecoverPassword else Check }
fields.turningOff = ???
fields.curRequest = {};
auto box = Box<PasscodeBox>(_session, fields);
box->boxClosing(
) | rpl::start_with_next([=] {
const auto weak = Ui::MakeWeak(this);
if (const auto onstack = _closeParent) {
onstack();
}
if (weak) {
weak->closeBox();
}
}, lifetime());
box->newPasswordSet(
) | rpl::start_with_next([=](QByteArray &&password) {
_newPasswordSet.fire(std::move(password));
}, lifetime());
getDelegate()->show(std::move(box));
} }
void RecoverBox::codeSubmitFail(const MTP::Error &error) { void RecoverBox::checkSubmitFail(const MTP::Error &error) {
if (MTP::IsFloodError(error)) { if (MTP::IsFloodError(error)) {
_submitRequest = 0; _submitRequest = 0;
setError(tr::lng_flood_error(tr::now)); setError(tr::lng_flood_error(tr::now));
@ -1229,7 +1315,7 @@ void RecoverBox::codeSubmitFail(const MTP::Error &error) {
const QString &err = error.type(); const QString &err = error.type();
if (err == qstr("PASSWORD_EMPTY")) { if (err == qstr("PASSWORD_EMPTY")) {
_passwordCleared.fire({}); _newPasswordSet.fire(QByteArray());
getDelegate()->show( getDelegate()->show(
Box<InformBox>(tr::lng_cloud_password_removed(tr::now)), Box<InformBox>(tr::lng_cloud_password_removed(tr::now)),
Ui::LayerOption::CloseOther); Ui::LayerOption::CloseOther);

View file

@ -11,6 +11,10 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "mtproto/sender.h" #include "mtproto/sender.h"
#include "core/core_cloud_password.h" #include "core/core_cloud_password.h"
namespace MTP {
class Instance;
} // namespace MTP
namespace Main { namespace Main {
class Session; class Session;
} // namespace Main } // namespace Main
@ -35,6 +39,7 @@ public:
Core::CloudPasswordCheckRequest curRequest; Core::CloudPasswordCheckRequest curRequest;
Core::CloudPasswordAlgo newAlgo; Core::CloudPasswordAlgo newAlgo;
bool hasRecovery = false; bool hasRecovery = false;
QString fromRecoveryCode;
bool notEmptyPassport = false; bool notEmptyPassport = false;
QString hint; QString hint;
Core::SecureSecretAlgo newSecureSecretAlgo; Core::SecureSecretAlgo newSecureSecretAlgo;
@ -47,6 +52,11 @@ public:
std::optional<QString> customDescription; std::optional<QString> customDescription;
rpl::producer<QString> customSubmitButton; rpl::producer<QString> customSubmitButton;
}; };
PasscodeBox(
QWidget*,
not_null<MTP::Instance*> mtp,
Main::Session *session,
const CloudFields &fields);
PasscodeBox( PasscodeBox(
QWidget*, QWidget*,
not_null<Main::Session*> session, not_null<Main::Session*> session,
@ -56,6 +66,8 @@ public:
rpl::producer<> passwordReloadNeeded() const; rpl::producer<> passwordReloadNeeded() const;
rpl::producer<> clearUnconfirmedPassword() const; rpl::producer<> clearUnconfirmedPassword() const;
rpl::producer<MTPauth_Authorization> newAuthorization() const;
bool handleCustomCheckError(const MTP::Error &error); bool handleCustomCheckError(const MTP::Error &error);
bool handleCustomCheckError(const QString &type); bool handleCustomCheckError(const QString &type);
@ -83,6 +95,9 @@ private:
bool onlyCheckCurrent() const; bool onlyCheckCurrent() const;
void setPasswordDone(const QByteArray &newPasswordBytes); void setPasswordDone(const QByteArray &newPasswordBytes);
void recoverPasswordDone(
const QByteArray &newPasswordBytes,
const MTPauth_Authorization &result);
void setPasswordFail(const MTP::Error &error); void setPasswordFail(const MTP::Error &error);
void setPasswordFail(const QString &type); void setPasswordFail(const QString &type);
void setPasswordFail( void setPasswordFail(
@ -132,7 +147,7 @@ private:
void passwordChecked(); void passwordChecked();
void serverError(); void serverError();
const not_null<Main::Session*> _session; Main::Session *_session = nullptr;
MTP::Sender _api; MTP::Sender _api;
QString _pattern; QString _pattern;
@ -163,6 +178,7 @@ private:
QString _oldError, _newError, _emailError; QString _oldError, _newError, _emailError;
rpl::event_stream<QByteArray> _newPasswordSet; rpl::event_stream<QByteArray> _newPasswordSet;
rpl::event_stream<MTPauth_Authorization> _newAuthorization;
rpl::event_stream<> _passwordReloadNeeded; rpl::event_stream<> _passwordReloadNeeded;
rpl::event_stream<> _clearUnconfirmedPassword; rpl::event_stream<> _clearUnconfirmedPassword;
@ -172,14 +188,14 @@ class RecoverBox final : public Ui::BoxContent {
public: public:
RecoverBox( RecoverBox(
QWidget*, QWidget*,
not_null<Main::Session*> session, not_null<MTP::Instance*> mtp,
Main::Session *session,
const QString &pattern, const QString &pattern,
bool notEmptyPassport, const PasscodeBox::CloudFields &fields,
bool hasPendingReset,
Fn<void()> closeParent = nullptr); Fn<void()> closeParent = nullptr);
rpl::producer<> passwordCleared() const; [[nodiscard]] rpl::producer<QByteArray> newPasswordSet() const;
rpl::producer<> recoveryExpired() const; [[nodiscard]] rpl::producer<> recoveryExpired() const;
//void reloadPassword(); //void reloadPassword();
//void recoveryExpired(); //void recoveryExpired();
@ -194,15 +210,17 @@ protected:
private: private:
void submit(); void submit();
void codeChanged(); void codeChanged();
void codeSubmitDone(const MTPauth_Authorization &result); void checkSubmitDone(const QString &code, const MTPBool &result);
void codeSubmitFail(const MTP::Error &error); void checkSubmitFail(const MTP::Error &error);
void setError(const QString &error); void setError(const QString &error);
Main::Session *_session = nullptr;
MTP::Sender _api; MTP::Sender _api;
mtpRequestId _submitRequest = 0; mtpRequestId _submitRequest = 0;
QString _pattern; QString _pattern;
bool _notEmptyPassport = false;
PasscodeBox::CloudFields _cloudFields;
object_ptr<Ui::InputField> _recoverCode; object_ptr<Ui::InputField> _recoverCode;
object_ptr<Ui::LinkButton> _noEmailAccess; object_ptr<Ui::LinkButton> _noEmailAccess;
@ -210,7 +228,7 @@ private:
QString _error; QString _error;
rpl::event_stream<> _passwordCleared; rpl::event_stream<QByteArray> _newPasswordSet;
rpl::event_stream<> _recoveryExpired; rpl::event_stream<> _recoveryExpired;
}; };

View file

@ -341,12 +341,12 @@ void CodeWidget::gotPassword(const MTPaccount_Password &result) {
stopCheck(); stopCheck();
_sentRequest = 0; _sentRequest = 0;
const auto &d = result.c_account_password(); const auto &d = result.c_account_password();
getData()->pwdRequest = Core::ParseCloudPasswordCheckRequest(d); getData()->pwdState = Core::ParseCloudPasswordState(d);
if (!d.vcurrent_algo() || !d.vsrp_id() || !d.vsrp_B()) { if (!d.vcurrent_algo() || !d.vsrp_id() || !d.vsrp_B()) {
LOG(("API Error: No current password received on login.")); LOG(("API Error: No current password received on login."));
_code->setFocus(); _code->setFocus();
return; return;
} else if (!getData()->pwdRequest) { } else if (!getData()->pwdState.request) {
const auto callback = [=](Fn<void()> &&close) { const auto callback = [=](Fn<void()> &&close) {
Core::UpdateApplication(); Core::UpdateApplication();
close(); close();
@ -357,9 +357,6 @@ void CodeWidget::gotPassword(const MTPaccount_Password &result) {
callback)); callback));
return; return;
} }
getData()->hasRecovery = d.is_has_recovery();
getData()->pwdHint = qs(d.vhint().value_or_empty());
getData()->pwdNotEmptyPassport = d.is_has_secure_values();
goReplace<PasswordCheckWidget>(Animate::Forward); goReplace<PasswordCheckWidget>(Animate::Forward);
} }
@ -381,10 +378,7 @@ void CodeWidget::submit() {
_checkRequestTimer.callEach(1000); _checkRequestTimer.callEach(1000);
_sentCode = text; _sentCode = text;
getData()->pwdRequest = Core::CloudPasswordCheckRequest(); getData()->pwdState = Core::CloudPasswordState();
getData()->hasRecovery = false;
getData()->pwdHint = QString();
getData()->pwdNotEmptyPassport = false;
_sentRequest = api().request(MTPauth_SignIn( _sentRequest = api().request(MTPauth_SignIn(
MTP_string(getData()->phone), MTP_string(getData()->phone),
MTP_bytes(getData()->phoneHash), MTP_bytes(getData()->phoneHash),

View file

@ -11,6 +11,7 @@ https://github.com/telegramdesktop/tdesktop/blob/master/LEGAL
#include "core/file_utilities.h" #include "core/file_utilities.h"
#include "core/core_cloud_password.h" #include "core/core_cloud_password.h"
#include "boxes/confirm_box.h" #include "boxes/confirm_box.h"
#include "boxes/passcode_box.h"
#include "lang/lang_keys.h" #include "lang/lang_keys.h"
#include "intro/intro_signup.h" #include "intro/intro_signup.h"
#include "ui/widgets/buttons.h" #include "ui/widgets/buttons.h"
@ -29,16 +30,13 @@ PasswordCheckWidget::PasswordCheckWidget(
not_null<Main::Account*> account, not_null<Main::Account*> account,
not_null<Data*> data) not_null<Data*> data)
: Step(parent, account, data) : Step(parent, account, data)
, _request(getData()->pwdRequest) , _passwordState(getData()->pwdState)
, _hasRecovery(getData()->hasRecovery)
, _notEmptyPassport(getData()->pwdNotEmptyPassport)
, _hint(getData()->pwdHint)
, _pwdField(this, st::introPassword, tr::lng_signin_password()) , _pwdField(this, st::introPassword, tr::lng_signin_password())
, _pwdHint(this, st::introPasswordHint) , _pwdHint(this, st::introPasswordHint)
, _codeField(this, st::introPassword, tr::lng_signin_code()) , _codeField(this, st::introPassword, tr::lng_signin_code())
, _toRecover(this, tr::lng_signin_recover(tr::now)) , _toRecover(this, tr::lng_signin_recover(tr::now))
, _toPassword(this, tr::lng_signin_try_password(tr::now)) { , _toPassword(this, tr::lng_signin_try_password(tr::now)) {
Expects(!!_request); Expects(!!_passwordState.request);
Lang::Updated( Lang::Updated(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
@ -53,11 +51,13 @@ PasswordCheckWidget::PasswordCheckWidget(
setTitleText(tr::lng_signin_title()); setTitleText(tr::lng_signin_title());
updateDescriptionText(); updateDescriptionText();
if (_hint.isEmpty()) { if (_passwordState.hint.isEmpty()) {
_pwdHint->hide(); _pwdHint->hide();
} else { } else {
_pwdHint->setText( _pwdHint->setText(tr::lng_signin_hint(
tr::lng_signin_hint(tr::now, lt_password_hint, _hint)); tr::now,
lt_password_hint,
_passwordState.hint));
} }
_codeField->hide(); _codeField->hide();
_toPassword->hide(); _toPassword->hide();
@ -73,9 +73,11 @@ void PasswordCheckWidget::refreshLang() {
_toPassword->setText( _toPassword->setText(
tr::lng_signin_try_password(tr::now)); tr::lng_signin_try_password(tr::now));
} }
if (!_hint.isEmpty()) { if (!_passwordState.hint.isEmpty()) {
_pwdHint->setText( _pwdHint->setText(tr::lng_signin_hint(
tr::lng_signin_hint(tr::now, lt_password_hint, _hint)); tr::now,
lt_password_hint,
_passwordState.hint));
} }
updateControlsGeometry(); updateControlsGeometry();
} }
@ -167,7 +169,7 @@ void PasswordCheckWidget::handleSrpIdInvalid() {
const auto now = crl::now(); const auto now = crl::now();
if (_lastSrpIdInvalidTime > 0 if (_lastSrpIdInvalidTime > 0
&& now - _lastSrpIdInvalidTime < Core::kHandleSrpIdInvalidTimeout) { && now - _lastSrpIdInvalidTime < Core::kHandleSrpIdInvalidTimeout) {
_request.id = 0; _passwordState.request.id = 0;
showError(rpl::single(Lang::Hard::ServerError())); showError(rpl::single(Lang::Hard::ServerError()));
} else { } else {
_lastSrpIdInvalidTime = now; _lastSrpIdInvalidTime = now;
@ -176,7 +178,7 @@ void PasswordCheckWidget::handleSrpIdInvalid() {
} }
void PasswordCheckWidget::checkPasswordHash() { void PasswordCheckWidget::checkPasswordHash() {
if (_request.id) { if (_passwordState.request.id) {
passwordChecked(); passwordChecked();
} else { } else {
requestPasswordData(); requestPasswordData();
@ -190,12 +192,8 @@ void PasswordCheckWidget::requestPasswordData() {
).done([=](const MTPaccount_Password &result) { ).done([=](const MTPaccount_Password &result) {
_sentRequest = 0; _sentRequest = 0;
result.match([&](const MTPDaccount_password &data) { result.match([&](const MTPDaccount_password &data) {
auto request = Core::ParseCloudPasswordCheckRequest(data); openssl::AddRandomSeed(bytes::make_span(data.vsecure_random().v));
if (request && request.id) { _passwordState = Core::ParseCloudPasswordState(data);
_request = std::move(request);
} else {
// Maybe the password was removed? Just submit it once again.
}
passwordChecked(); passwordChecked();
}); });
}).send(); }).send();
@ -203,12 +201,12 @@ void PasswordCheckWidget::requestPasswordData() {
void PasswordCheckWidget::passwordChecked() { void PasswordCheckWidget::passwordChecked() {
const auto check = Core::ComputeCloudPasswordCheck( const auto check = Core::ComputeCloudPasswordCheck(
_request, _passwordState.request,
_passwordHash); _passwordHash);
if (!check) { if (!check) {
return serverError(); return serverError();
} }
_request.id = 0; _passwordState.request.id = 0;
_sentRequest = api().request( _sentRequest = api().request(
MTPauth_CheckPassword(check.result) MTPauth_CheckPassword(check.result)
).done([=](const MTPauth_Authorization &result) { ).done([=](const MTPauth_Authorization &result) {
@ -222,6 +220,23 @@ void PasswordCheckWidget::serverError() {
showError(rpl::single(Lang::Hard::ServerError())); showError(rpl::single(Lang::Hard::ServerError()));
} }
void PasswordCheckWidget::codeSubmitDone(
const QString &code,
const MTPBool &result) {
auto fields = PasscodeBox::CloudFields::From(_passwordState);
fields.fromRecoveryCode = code;
fields.hasRecovery = false;
fields.curRequest = {};
auto box = Box<PasscodeBox>(&api().instance(), nullptr, fields);
box->newAuthorization(
) | rpl::start_with_next([=](const MTPauth_Authorization &result) {
pwdSubmitDone(true, result);
}, lifetime());
Ui::show(std::move(box));
}
void PasswordCheckWidget::codeSubmitFail(const MTP::Error &error) { void PasswordCheckWidget::codeSubmitFail(const MTP::Error &error) {
if (MTP::IsFloodError(error)) { if (MTP::IsFloodError(error)) {
showError(tr::lng_flood_error()); showError(tr::lng_flood_error());
@ -269,7 +284,7 @@ void PasswordCheckWidget::recoverStartFail(const MTP::Error &error) {
} }
void PasswordCheckWidget::toRecover() { void PasswordCheckWidget::toRecover() {
if (_hasRecovery) { if (_passwordState.hasRecovery) {
if (_sentRequest) { if (_sentRequest) {
api().request(base::take(_sentRequest)).cancel(); api().request(base::take(_sentRequest)).cancel();
} }
@ -339,18 +354,16 @@ void PasswordCheckWidget::submit() {
return; return;
} }
const auto send = crl::guard(this, [=] { const auto send = crl::guard(this, [=] {
_sentRequest = api().request(MTPauth_RecoverPassword( _sentRequest = api().request(MTPauth_CheckRecoveryPassword(
MTP_flags(0), MTP_string(code)
MTP_string(code), )).done([=](const MTPBool &result) {
MTPaccount_PasswordInputSettings() codeSubmitDone(code, result);
)).done([=](const MTPauth_Authorization &result) {
pwdSubmitDone(true, result);
}).fail([=](const MTP::Error &error) { }).fail([=](const MTP::Error &error) {
codeSubmitFail(error); codeSubmitFail(error);
}).handleFloodErrors().send(); }).handleFloodErrors().send();
}); });
if (_notEmptyPassport) { if (_passwordState.notEmptyPassport) {
const auto confirmed = [=](Fn<void()> &&close) { const auto confirmed = [=](Fn<void()> &&close) {
send(); send();
close(); close();
@ -367,7 +380,7 @@ void PasswordCheckWidget::submit() {
const auto password = _pwdField->getLastText().toUtf8(); const auto password = _pwdField->getLastText().toUtf8();
_passwordHash = Core::ComputeCloudPasswordHash( _passwordHash = Core::ComputeCloudPasswordHash(
_request.algo, _passwordState.request.algo,
bytes::make_span(password)); bytes::make_span(password));
checkPasswordHash(); checkPasswordHash();
} }

View file

@ -50,6 +50,7 @@ private:
void pwdSubmitDone(bool recover, const MTPauth_Authorization &result); void pwdSubmitDone(bool recover, const MTPauth_Authorization &result);
void pwdSubmitFail(const MTP::Error &error); void pwdSubmitFail(const MTP::Error &error);
void codeSubmitDone(const QString &code, const MTPBool &result);
void codeSubmitFail(const MTP::Error &error); void codeSubmitFail(const MTP::Error &error);
void recoverStartFail(const MTP::Error &error); void recoverStartFail(const MTP::Error &error);
@ -62,12 +63,10 @@ private:
void passwordChecked(); void passwordChecked();
void serverError(); void serverError();
Core::CloudPasswordCheckRequest _request; Core::CloudPasswordState _passwordState;
crl::time _lastSrpIdInvalidTime = 0; crl::time _lastSrpIdInvalidTime = 0;
bytes::vector _passwordHash; bytes::vector _passwordHash;
bool _hasRecovery = false; QString _emailPattern;
bool _notEmptyPassport = false;
QString _hint, _emailPattern;
object_ptr<Ui::PasswordInput> _pwdField; object_ptr<Ui::PasswordInput> _pwdField;
object_ptr<Ui::FlatLabel> _pwdHint; object_ptr<Ui::FlatLabel> _pwdHint;

View file

@ -391,13 +391,12 @@ void QrWidget::sendCheckPasswordRequest() {
_requestId = api().request(MTPaccount_GetPassword( _requestId = api().request(MTPaccount_GetPassword(
)).done([=](const MTPaccount_Password &result) { )).done([=](const MTPaccount_Password &result) {
result.match([&](const MTPDaccount_password &data) { result.match([&](const MTPDaccount_password &data) {
getData()->pwdRequest = Core::ParseCloudPasswordCheckRequest( getData()->pwdState = Core::ParseCloudPasswordState(data);
data);
if (!data.vcurrent_algo() || !data.vsrp_id() || !data.vsrp_B()) { if (!data.vcurrent_algo() || !data.vsrp_id() || !data.vsrp_B()) {
LOG(("API Error: No current password received on login.")); LOG(("API Error: No current password received on login."));
goReplace<QrWidget>(Animate::Forward); goReplace<QrWidget>(Animate::Forward);
return; return;
} else if (!getData()->pwdRequest) { } else if (!getData()->pwdState.request) {
const auto callback = [=](Fn<void()> &&close) { const auto callback = [=](Fn<void()> &&close) {
Core::UpdateApplication(); Core::UpdateApplication();
close(); close();
@ -408,9 +407,6 @@ void QrWidget::sendCheckPasswordRequest() {
callback)); callback));
return; return;
} }
getData()->hasRecovery = data.is_has_recovery();
getData()->pwdHint = qs(data.vhint().value_or_empty());
getData()->pwdNotEmptyPassport = data.is_has_secure_values();
goReplace<PasswordCheckWidget>(Animate::Forward); goReplace<PasswordCheckWidget>(Animate::Forward);
}); });
}).fail([=](const MTP::Error &error) { }).fail([=](const MTP::Error &error) {

View file

@ -56,10 +56,7 @@ struct Data {
int codeLength = 5; int codeLength = 5;
bool codeByTelegram = false; bool codeByTelegram = false;
Core::CloudPasswordCheckRequest pwdRequest; Core::CloudPasswordState pwdState;
bool hasRecovery = false;
QString pwdHint;
bool pwdNotEmptyPassport = false;
Window::TermsLock termsLock; Window::TermsLock termsLock;

View file

@ -1000,15 +1000,25 @@ void FormController::recoverPassword() {
const auto &data = result.c_auth_passwordRecovery(); const auto &data = result.c_auth_passwordRecovery();
const auto pattern = qs(data.vemail_pattern()); const auto pattern = qs(data.vemail_pattern());
auto fields = PasscodeBox::CloudFields{
.newAlgo = _password.newAlgo,
.hasRecovery = _password.hasRecovery,
.newSecureSecretAlgo = _password.newSecureAlgo,
.pendingResetDate = _password.pendingResetDate,
};
const auto box = _view->show(Box<RecoverBox>( const auto box = _view->show(Box<RecoverBox>(
&_controller->session().mtp(),
&_controller->session(), &_controller->session(),
pattern, pattern,
_password.notEmptyPassport, fields));
_password.pendingResetDate != 0));
box->passwordCleared( box->newPasswordSet(
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=](const QByteArray &password) {
reloadPassword(); if (password.isEmpty()) {
reloadPassword();
} else {
reloadAndSubmitPassword(password);
}
}, box->lifetime()); }, box->lifetime());
box->recoveryExpired( box->recoveryExpired(

View file

@ -684,23 +684,23 @@ void PanelController::setupPassword() {
return; return;
} }
auto fields = PasscodeBox::CloudFields(); auto fields = PasscodeBox::CloudFields{
fields.newAlgo = settings.newAlgo; .newAlgo = settings.newAlgo,
fields.newSecureSecretAlgo = settings.newSecureAlgo; .hasRecovery = settings.hasRecovery,
.newSecureSecretAlgo = settings.newSecureAlgo,
.pendingResetDate = settings.pendingResetDate,
};
auto box = show(Box<PasscodeBox>(&_form->window()->session(), fields)); auto box = show(Box<PasscodeBox>(&_form->window()->session(), fields));
box->newPasswordSet( box->newPasswordSet(
) | rpl::filter([=](const QByteArray &password) { ) | rpl::start_with_next([=](const QByteArray &password) {
return !password.isEmpty(); if (password.isEmpty()) {
}) | rpl::start_with_next([=](const QByteArray &password) { _form->reloadPassword();
_form->reloadAndSubmitPassword(password); } else {
_form->reloadAndSubmitPassword(password);
}
}, box->lifetime()); }, box->lifetime());
rpl::merge( box->passwordReloadNeeded(
box->passwordReloadNeeded(),
box->newPasswordSet(
) | rpl::filter([=](const QByteArray &password) {
return password.isEmpty();
}) | rpl::to_empty
) | rpl::start_with_next([=] { ) | rpl::start_with_next([=] {
_form->reloadPassword(); _form->reloadPassword();
}, box->lifetime()); }, box->lifetime());