GH-3392 Add recognition of already migrated Mojang accounts

This commit is contained in:
Petr Mrázek 2021-08-29 19:58:35 +02:00
parent 1e1655bc4b
commit 7239502675
6 changed files with 97 additions and 41 deletions

View File

@ -183,6 +183,19 @@ void LaunchController::login() {
emitFailed(errorString); emitFailed(errorString);
return; return;
} }
case AuthSession::GoneOrMigrated: {
auto errorString = tr("The account no longer exists on the servers. It may have been migrated, in which case please add the new account you migrated this one to.");
QMessageBox::warning(
nullptr,
tr("Account gone"),
errorString,
QMessageBox::StandardButton::Ok,
QMessageBox::StandardButton::Ok
);
tryagain = false;
emitFailed(errorString);
return;
}
case AuthSession::PlayableOffline: { case AuthSession::PlayableOffline: {
// we ask the user for a player name // we ask the user for a player name
bool ok = false; bool ok = false;

View File

@ -49,6 +49,8 @@ QString AccountTask::getStateMessage() const
return tr("Failed to contact the authentication server."); return tr("Failed to contact the authentication server.");
case STATE_FAILED_HARD: case STATE_FAILED_HARD:
return tr("Failed to authenticate."); return tr("Failed to authenticate.");
case STATE_FAILED_GONE:
return tr("Failed to authenticate. The account no longer exists.");
default: default:
return tr("..."); return tr("...");
} }
@ -62,7 +64,7 @@ void AccountTask::changeState(AccountTask::State newState, QString reason)
{ {
emitSucceeded(); emitSucceeded();
} }
else if (newState == STATE_FAILED_HARD || newState == STATE_FAILED_SOFT) else if (newState == STATE_FAILED_HARD || newState == STATE_FAILED_SOFT || newState == STATE_FAILED_GONE)
{ {
emitFailed(reason); emitFailed(reason);
} }

View File

@ -76,6 +76,7 @@ public:
STATE_WORKING, STATE_WORKING,
STATE_FAILED_SOFT, //!< soft failure. this generally means the user auth details haven't been invalidated STATE_FAILED_SOFT, //!< soft failure. this generally means the user auth details haven't been invalidated
STATE_FAILED_HARD, //!< hard failure. auth is invalid STATE_FAILED_HARD, //!< hard failure. auth is invalid
STATE_FAILED_GONE, //!< hard failure. auth is invalid, and the account no longer exists
STATE_SUCCEEDED STATE_SUCCEEDED
} m_accountState = STATE_CREATED; } m_accountState = STATE_CREATED;

View File

@ -18,7 +18,8 @@ struct AuthSession
RequiresOAuth, RequiresOAuth,
RequiresPassword, RequiresPassword,
PlayableOffline, PlayableOffline,
PlayableOnline PlayableOnline,
GoneOrMigrated
} status = Undetermined; } status = Undetermined;
// client token // client token

View File

@ -227,32 +227,60 @@ void MinecraftAccount::authFailed(QString reason)
auto session = m_currentTask->getAssignedSession(); auto session = m_currentTask->getAssignedSession();
// This is emitted when the yggdrasil tasks time out or are cancelled. // This is emitted when the yggdrasil tasks time out or are cancelled.
// -> we treat the error as no-op // -> we treat the error as no-op
if (m_currentTask->accountState() == AccountTask::STATE_FAILED_SOFT) switch (m_currentTask->accountState()) {
{ case AccountTask::STATE_FAILED_SOFT: {
if (session) if (session)
{ {
session->status = accountStatus() == Verified ? AuthSession::PlayableOffline : AuthSession::RequiresPassword; if(accountStatus() == Verified) {
session->auth_server_online = false; session->status = AuthSession::PlayableOffline;
fillSession(session); }
else {
if(data.type == AccountType::MSA) {
session->status = AuthSession::RequiresOAuth;
}
else {
session->status = AuthSession::RequiresPassword;
}
}
session->auth_server_online = false;
fillSession(session);
}
} }
} break;
else case AccountTask::STATE_FAILED_HARD: {
{ // FIXME: MSA data clearing
// FIXME: MSA ... data.yggdrasilToken.token = QString();
data.yggdrasilToken.token = QString(); data.yggdrasilToken.validity = Katabasis::Validity::None;
data.yggdrasilToken.validity = Katabasis::Validity::None; data.validity_ = Katabasis::Validity::None;
data.validity_ = Katabasis::Validity::None; emit changed();
emit changed(); if (session)
if (session) {
{ if(data.type == AccountType::MSA) {
if(data.type == AccountType::MSA) { session->status = AuthSession::RequiresOAuth;
session->status = AuthSession::RequiresOAuth; }
else {
session->status = AuthSession::RequiresPassword;
}
session->auth_server_online = true;
fillSession(session);
} }
else { }
session->status = AuthSession::RequiresPassword; break;
case AccountTask::STATE_FAILED_GONE: {
data.validity_ = Katabasis::Validity::None;
emit changed();
if (session)
{
session->status = AuthSession::GoneOrMigrated;
session->auth_server_online = true;
fillSession(session);
} }
session->auth_server_online = true; }
fillSession(session); break;
case AccountTask::STATE_CREATED:
case AccountTask::STATE_WORKING:
case AccountTask::STATE_SUCCEEDED: {
// Not reachable here, as they are not failures.
} }
} }
m_currentTask.reset(); m_currentTask.reset();

View File

@ -255,10 +255,17 @@ void Yggdrasil::processReply()
case QNetworkReply::ContentAccessDenied: case QNetworkReply::ContentAccessDenied:
case QNetworkReply::ContentOperationNotPermittedError: case QNetworkReply::ContentOperationNotPermittedError:
break; break;
case QNetworkReply::ContentGoneError: {
changeState(
STATE_FAILED_GONE,
tr("The Mojang account no longer exists. It may have been migrated to a Microsoft account.")
);
}
default: default:
changeState(STATE_FAILED_SOFT, changeState(
tr("Authentication operation failed due to a network error: %1 (%2)") STATE_FAILED_SOFT,
.arg(m_netReply->errorString()).arg(m_netReply->error())); tr("Authentication operation failed due to a network error: %1 (%2)").arg(m_netReply->errorString()).arg(m_netReply->error())
);
return; return;
} }
@ -283,10 +290,10 @@ void Yggdrasil::processReply()
} }
else else
{ {
changeState(STATE_FAILED_SOFT, tr("Failed to parse authentication server response " changeState(
"JSON response: %1 at offset %2.") STATE_FAILED_SOFT,
.arg(jsonError.errorString()) tr("Failed to parse authentication server response JSON response: %1 at offset %2.").arg(jsonError.errorString()).arg(jsonError.offset)
.arg(jsonError.offset)); );
qCritical() << replyData; qCritical() << replyData;
} }
return; return;
@ -301,19 +308,18 @@ void Yggdrasil::processReply()
// We were able to parse the server's response. Woo! // We were able to parse the server's response. Woo!
// Call processError. If a subclass has overridden it then they'll handle their // Call processError. If a subclass has overridden it then they'll handle their
// stuff there. // stuff there.
qDebug() << "The request failed, but the server gave us an error message. " qDebug() << "The request failed, but the server gave us an error message. Processing error.";
"Processing error.";
processError(doc.object()); processError(doc.object());
} }
else else
{ {
// The server didn't say anything regarding the error. Give the user an unknown // The server didn't say anything regarding the error. Give the user an unknown
// error. // error.
qDebug() qDebug() << "The request failed and the server gave no error message. Unknown error.";
<< "The request failed and the server gave no error message. Unknown error."; changeState(
changeState(STATE_FAILED_SOFT, STATE_FAILED_SOFT,
tr("An unknown error occurred when trying to communicate with the " tr("An unknown error occurred when trying to communicate with the authentication server: %1").arg(m_netReply->errorString())
"authentication server: %1").arg(m_netReply->errorString())); );
} }
} }
@ -325,8 +331,13 @@ void Yggdrasil::processError(QJsonObject responseData)
if (errorVal.isString() && errorMessageValue.isString()) if (errorVal.isString() && errorMessageValue.isString())
{ {
m_error = std::shared_ptr<Error>(new Error{ m_error = std::shared_ptr<Error>(
errorVal.toString(""), errorMessageValue.toString(""), causeVal.toString("")}); new Error {
errorVal.toString(""),
errorMessageValue.toString(""),
causeVal.toString("")
}
);
changeState(STATE_FAILED_HARD, m_error->m_errorMessageVerbose); changeState(STATE_FAILED_HARD, m_error->m_errorMessageVerbose);
} }
else else