From a8c9c34bead54d469f126db194b571e2fe90b078 Mon Sep 17 00:00:00 2001 From: George Hazan Date: Sun, 14 Jan 2024 18:43:24 +0300 Subject: Discord: add MFA support --- protocols/Discord/src/http.cpp | 2 +- protocols/Discord/src/mfa.cpp | 112 ++++++++++++++++++++++++++++++++++++++ protocols/Discord/src/options.cpp | 4 +- protocols/Discord/src/proto.h | 10 ++++ protocols/Discord/src/resource.h | 10 +++- protocols/Discord/src/server.cpp | 17 ++++-- 6 files changed, 143 insertions(+), 12 deletions(-) create mode 100644 protocols/Discord/src/mfa.cpp (limited to 'protocols/Discord/src') diff --git a/protocols/Discord/src/http.cpp b/protocols/Discord/src/http.cpp index 82e8d495d0..70cfe9db80 100644 --- a/protocols/Discord/src/http.cpp +++ b/protocols/Discord/src/http.cpp @@ -41,7 +41,7 @@ static LONG g_reqNum = 0; AsyncHttpRequest::AsyncHttpRequest(CDiscordProto *ppro, int iRequestType, LPCSTR _url, MTHttpRequestHandler pFunc, JSONNode *pRoot) { if (*_url == '/') { // relative url leads to a site - m_szUrl = "https://discord.com/api/v8"; + m_szUrl = "https://discord.com/api/v9"; m_szUrl += _url; m_bMainSite = true; } diff --git a/protocols/Discord/src/mfa.cpp b/protocols/Discord/src/mfa.cpp new file mode 100644 index 0000000000..9de79283fa --- /dev/null +++ b/protocols/Discord/src/mfa.cpp @@ -0,0 +1,112 @@ +/* +Copyright © 2016-22 Miranda NG team + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 2 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ + +#include "stdafx.h" + +///////////////////////////////////////////////////////////////////////////////////////// + +class CMfaDialog : public CDiscordDlgBase +{ + bool m_bHasSms, m_bHasTotp, m_bUseTotp = true; + CMStringA m_szTicket; + + CCtrlBase m_label; + CCtrlEdit edtCode; + CCtrlButton btnCancel, btnAnother; + +public: + CMfaDialog(CDiscordProto *ppro, const JSONNode &pRoot) : + CDiscordDlgBase(ppro, IDD_MFA), + m_label(this, IDC_LABEL), + edtCode(this, IDC_CODE), + btnCancel(this, IDCANCEL), + btnAnother(this, IDC_ANOTHER) + { + m_bHasSms = pRoot["sms"].as_bool(); + m_bHasTotp = pRoot["totp"].as_bool(); + m_szTicket = pRoot["ticket"].as_mstring(); + } + + bool OnInitDialog() override + { + if (m_bUseTotp) + m_label.SetText(TranslateT("Enter Discord verification code:")); + else + m_label.SetText(TranslateT("Enter SMS code")); + + // if (!m_bHasSms) + btnAnother.Disable(); + return true; + } + + bool OnApply() override + { + ptrW wszCode(edtCode.GetText()); + + if (m_bUseTotp) { + JSONNode root; + root << WCHAR_PARAM("code", wszCode) << CHAR_PARAM("ticket", m_szTicket); + + auto *pReq = new AsyncHttpRequest(m_proto, REQUEST_POST, "/auth/mfa/totp", &CDiscordProto::OnSendTotp, &root); + pReq->pUserInfo = this; + m_proto->Push(pReq); + } + return false; + } + + void onClick_Cancel() + { + m_proto->ConnectionFailed(LOGINERR_WRONGPASSWORD); + } + + void WrongCode() + { + edtCode.SetText(L""); + Beep(470, 200); + } +}; + +static void CALLBACK LaunchDialog(void *param) +{ + ((CMfaDialog *)param)->Show(); +} + +void CDiscordProto::ShowMfaDialog(const JSONNode &pRoot) +{ + CallFunctionAsync(LaunchDialog, new CMfaDialog(this, pRoot)); +} + +///////////////////////////////////////////////////////////////////////////////////////// + +void CDiscordProto::OnSendTotp(MHttpResponse *pReply, struct AsyncHttpRequest *pReq) +{ + auto *pDlg = (CMfaDialog *)pReq->pUserInfo; + + JsonReply root(pReply); + if (!root) { + pDlg->WrongCode(); + return; + } + + pDlg->Close(); + + auto &data = root.data(); + CMStringA szToken = data["token"].as_mstring(); + m_szAccessToken = szToken.Detach(); + setString("AccessToken", m_szAccessToken); + RetrieveMyInfo(); +} diff --git a/protocols/Discord/src/options.cpp b/protocols/Discord/src/options.cpp index 4716584b96..dc4a6d60b5 100644 --- a/protocols/Discord/src/options.cpp +++ b/protocols/Discord/src/options.cpp @@ -19,7 +19,7 @@ along with this program. If not, see . ///////////////////////////////////////////////////////////////////////////////////////// -class CDiscardAccountOptions : public CProtoDlgBase +class CDiscardAccountOptions : public CDiscordDlgBase { CCtrlCheck chkUseChats, chkHideChats, chkUseGroups, chkDeleteMsgs; CCtrlEdit m_edGroup, m_edUserName, m_edPassword; @@ -27,7 +27,7 @@ class CDiscardAccountOptions : public CProtoDlgBase public: CDiscardAccountOptions(CDiscordProto *ppro, int iDlgID, bool bFullDlg) : - CProtoDlgBase(ppro, iDlgID), + CDiscordDlgBase(ppro, iDlgID), m_edGroup(this, IDC_GROUP), m_edUserName(this, IDC_USERNAME), m_edPassword(this, IDC_PASSWORD), diff --git a/protocols/Discord/src/proto.h b/protocols/Discord/src/proto.h index fdb5e6a33b..bb99d2efbc 100644 --- a/protocols/Discord/src/proto.h +++ b/protocols/Discord/src/proto.h @@ -227,6 +227,7 @@ class CDiscordProto : public PROTO { friend struct AsyncHttpRequest; friend class CDiscardAccountOptions; + friend class CMfaDialog; class CDiscordProtoImpl { @@ -394,6 +395,13 @@ class CDiscordProto : public PROTO void ProcessChatUser(CDiscordUser *pChat, SnowFlake userId, const JSONNode &pRoot); void ParseSpecialChars(SESSION_INFO *si, CMStringW &str); + ////////////////////////////////////////////////////////////////////////////////////// + // two-factor auth + + void ShowMfaDialog(const JSONNode &pRoot); + + void OnSendTotp(MHttpResponse *, struct AsyncHttpRequest *); + ////////////////////////////////////////////////////////////////////////////////////// // misc methods @@ -526,6 +534,8 @@ public: void CheckAvatarChange(MCONTACT hContact, const CMStringW &wszNewHash); }; +typedef CProtoDlgBase CDiscordDlgBase; + ///////////////////////////////////////////////////////////////////////////////////////// struct CMPlugin : public ACCPROTOPLUGIN diff --git a/protocols/Discord/src/resource.h b/protocols/Discord/src/resource.h index 099a4af3af..dc1c9706f1 100644 --- a/protocols/Discord/src/resource.h +++ b/protocols/Discord/src/resource.h @@ -1,6 +1,6 @@ //{{NO_DEPENDENCIES}} // Microsoft Visual C++ generated include file. -// Used by w:\miranda-ng\protocols\Discord\res\discord.rc +// Used by W:\miranda-ng\protocols\Discord\res\discord.rc // #define IDI_MAIN 101 #define IDI_GROUPCHAT 102 @@ -9,6 +9,7 @@ #define IDD_OPTIONS_ACCMGR 105 #define IDI_VOICE_CALL 106 #define IDI_VOICE_ENDED 107 +#define IDD_MFA 108 #define IDC_PASSWORD 1001 #define IDC_USERNAME 1002 #define IDC_GROUP 1003 @@ -16,15 +17,18 @@ #define IDC_HIDECHATS 1005 #define IDC_USEGROUPS 1006 #define IDC_USEGUILDS 1007 +#define IDC_CODE 1008 #define IDC_DELETE_MSGS 1009 +#define IDC_ANOTHER 1009 +#define IDC_LABEL 1010 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS -#define _APS_NEXT_RESOURCE_VALUE 104 +#define _APS_NEXT_RESOURCE_VALUE 108 #define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1008 +#define _APS_NEXT_CONTROL_VALUE 1011 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif diff --git a/protocols/Discord/src/server.cpp b/protocols/Discord/src/server.cpp index 104d45435d..75167a7a60 100644 --- a/protocols/Discord/src/server.cpp +++ b/protocols/Discord/src/server.cpp @@ -281,13 +281,18 @@ void CDiscordProto::OnReceiveToken(MHttpResponse *pReply, AsyncHttpRequest*) } auto &data = root.data(); - CMStringA szToken = data["token"].as_mstring(); - if (szToken.IsEmpty()) { - debugLogA("Strange empty token received, exiting"); + if (auto &token = data["token"]) { + CMStringA szToken = token.as_mstring(); + m_szAccessToken = szToken.Detach(); + setString("AccessToken", m_szAccessToken); + RetrieveMyInfo(); return; } - m_szAccessToken = szToken.Detach(); - setString("AccessToken", m_szAccessToken); - RetrieveMyInfo(); + if (data["mfa"].as_bool()) + ShowMfaDialog(data); + else { + ConnectionFailed(LOGINERR_WRONGPASSWORD); + debugLogA("Strange empty token received, exiting"); + } } -- cgit v1.2.3