Skip to content

Commit

Permalink
To be able to login with 2FA enabled, we need to use clientlogin inst…
Browse files Browse the repository at this point in the history
…ead of login.

In case of 2fa, a second http request is needed to send user, pass, totp (2fa code), fake loginreturnurl, rememberMe boolean and our classical token.

It's the first commit, currently the login form works as usual for normal users. users with 2fa enabled want be connected yet.

**So this commit is not ready to be merged for now.**

Inspired by https://github.com/commons-app/apps-android-commons/blob/b0e8175003a686789474238dd293aa89d1e925c7/app/src/main/java/fr/free/nrw/commons/mwapi/ApacheHttpClientMediaWikiApi.java#L93

Bug: https://phabricator.wikimedia.org/T180279
  • Loading branch information
framawiki committed Nov 11, 2017
1 parent 2e53b2c commit 85f0612
Show file tree
Hide file tree
Showing 4 changed files with 69 additions and 33 deletions.
1 change: 1 addition & 0 deletions huggle/Localization/en.xml
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,7 @@
<string name="login-ro-title">Switch to read-only</string>
<string name="login-ro-question">One or more projects ($1) do not allow you to login with edit permissions (reason: $2). Do you want to switch to read-only mode instead?</string>
<string name="login-ro-info">Project $1 switched to read-only mode</string>
<string name="not-implemented">This functionality is not yet implemented. Please contact developers at huggle@lists.wikimedia.org.</string>
<string name="main-stat">$1 edits per minute, $2 reverts per minute, level $3</string>
<string name="main-menu-provider-stop">Stop provider</string>
<string name="main-menu-provider-resume">Resume provider</string>
Expand Down
5 changes: 5 additions & 0 deletions huggle/apiquery.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -407,6 +407,7 @@ void ApiQuery::SetAction(const Action action)
this->ActionPart = "clearhasmsg";
this->UsingPOST = true;
return;
///! \todo ActionQuery still used ?
case ActionQuery:
this->ActionPart = "query";
this->IsContinuous = true;
Expand All @@ -416,6 +417,10 @@ void ApiQuery::SetAction(const Action action)
this->ActionPart = "login";
this->EnforceLogin = false;
return;
case ClientLogin:
this->ActionPart = "clientlogin";
this->EnforceLogin = false;
return;
case ActionLogout:
this->ActionPart = "logout";
return;
Expand Down
1 change: 1 addition & 0 deletions huggle/apiquery.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ namespace Huggle
ActionClearHasMsg,
ActionQuery,
ActionLogin,
ClientLogin,
ActionLogout,
//ActionTokens,
ActionPurge,
Expand Down
95 changes: 62 additions & 33 deletions huggle/login.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
#include <QSslSocket>
#include <QDesktopServices>
#include <QMessageBox>
#include <QInputDialog>

#define LOGINFORM_LOGIN 0
#define LOGINFORM_SITEINFO 1
Expand Down Expand Up @@ -554,21 +555,20 @@ void Login::PerformLoginPart2(WikiSite *site)
this->Statuses[site] = WaitingForToken;
this->LoginQueries.remove(site);
query->DecRef();
query = new ApiQuery(ActionLogin, site);
query = new ApiQuery(ClientLogin, site);
this->LoginQueries.insert(site, query);
query->HiddenQuery = true;
//query->HiddenQuery = true;
query->IncRef();
if (hcfg->SystemConfig_BotPassword)
{
query->Parameters = "lgname=" + QUrl::toPercentEncoding(hcfg->SystemConfig_BotLogin)
+ "&lgpassword=" + QUrl::toPercentEncoding(hcfg->TemporaryConfig_Password)
+ "&lgtoken=" + QUrl::toPercentEncoding(token);
query->Parameters = "username=" + QUrl::toPercentEncoding(hcfg->SystemConfig_BotLogin)
+ "&password=" + QUrl::toPercentEncoding(hcfg->TemporaryConfig_Password);
} else
{
query->Parameters = "lgname=" + QUrl::toPercentEncoding(hcfg->SystemConfig_Username)
+ "&lgpassword=" + QUrl::toPercentEncoding(hcfg->TemporaryConfig_Password)
+ "&lgtoken=" + QUrl::toPercentEncoding(token);
query->Parameters = "username=" + QUrl::toPercentEncoding(hcfg->SystemConfig_Username)
+ "&password=" + QUrl::toPercentEncoding(hcfg->TemporaryConfig_Password);
}
query->Parameters = query->Parameters + "&loginreturnurl=http://example.com/&rememberMe=1&logintoken=" + QUrl::toPercentEncoding(token);
query->UsingPOST = true;
query->Process();
}
Expand Down Expand Up @@ -1269,42 +1269,71 @@ bool Login::ProcessOutput(WikiSite *site)
ApiQuery *query = this->LoginQueries[site];
// Check what the result was
ApiQueryResult *result = query->GetApiQueryResult();
ApiQueryResultNode *ln = result->GetNode("login");
QString result_code = ln->GetAttribute("result");
QString reason = ln->GetAttribute("reason");
if (result_code.isEmpty())
ApiQueryResultNode *ln = result->GetNode("clientlogin");
QString status = ln->GetAttribute("status");
if (status.isEmpty())
{
this->DisplayError(_l("api.php-invalid-response"));
return false;
}
if (result_code == "Success")

if (status == "PASS")
return true;
if (result_code == "EmptyPass")
{
this->DisplayError(_l("login-password-empty"));
if (status == "UI") {
// Need a user interaction like captacha or 2FA
//QString v_id = ln->ChildNodes.at(0)->GetAttribute("id", "unknown");
//if (v_id == "TOTPAuthenticationRequest"){
if (true){
// 2FA is requierd (TOTP code needed)
QString totp = QInputDialog::getText(this, "Two factor authentification", "Please enter the 2FA code from your device:");
query = new ApiQuery(ClientLogin, site);
//query->HiddenQuery = true;
query->IncRef();
query->Parameters = "username=" + QUrl::toPercentEncoding(hcfg->SystemConfig_BotLogin)
+ "&password=" + QUrl::toPercentEncoding(hcfg->TemporaryConfig_Password)
+ "&OATHToken=" + totp + "&loginreturnurl=http://example.com/&rememberMe=1&logintoken=" + QUrl::toPercentEncoding(this->Tokens[site]);
query->UsingPOST = true;
query->Process();
ApiQueryResult *result = query->GetApiQueryResult();
ApiQueryResultNode *ln = result->GetNode("clientlogin");
}
return false;
}
if (result_code == "WrongPass")
{
/// \bug This sometimes doesn't work properly
this->ui->lineEdit_password->setFocus();
this->DisplayError(_l("login-error-password"));
if (status == "REDIRECT")
// Need to login using another web service
this->DisplayError(_l("not-implemented"));
return false;
}
if (result_code == "NoName")
if (status == "FAIL")
{
this->DisplayError(_l("login-fail-wrong-name"));
return false;
}
if (result_code == "NotExists")
{
this->DisplayError(_l("login-username-doesnt-exist"));
QString message = ln->GetAttribute("message");
QString message_code = ln->GetAttribute("messagecode");
if (message_code == "wrongpassword") {
/// \bug This sometimes doesn't work properly
this->ui->lineEdit_password->setFocus();
this->DisplayError(_l("login-error-password"));
return false;
}
/// \todo Verify these error codes
if (message_code == "EmptyPass")
{
this->DisplayError(_l("login-password-empty"));
return false;
}
if (message_code == "NoName")
{
this->DisplayError(_l("login-fail-wrong-name"));
return false;
}
if (message_code == "NotExists")
{
this->DisplayError(_l("login-username-doesnt-exist"));
return false;
}
if (message.isEmpty())
message = message_code;
this->DisplayError(_l("login-api", message));
return false;
}
if (reason.isEmpty())
reason = result_code;
this->DisplayError(_l("login-api", reason));
return false;
}

void Login::on_ButtonOK_clicked()
Expand Down

0 comments on commit 85f0612

Please sign in to comment.