У нас возникла проблема со входом в Gmail с помощью Indy TIdSMTP

Я использую компоненты Indy в C++Builder 11.3, чтобы попытаться отправить электронное письмо из нашего проекта RAD Server. В целях тестирования я написал приведенный ниже 64-битный код и получаю ошибку, которая уже рассматривалась Реми в его ответе на «Ошибка согласования SSL» в Delphi 11.

Я перенес код точно из поста Реми, за исключением предостережений по C++, и получаю следующую ошибку:

Имя пользователя и пароль не принимаются

и URL-адрес, указывающий на «support.google.com.../mail/?p=Bad Credentials».

Мне интересно, связана ли проблема с версиями SSL/TLS?

Если кто-то заметит в коде что-то не так, пожалуйста, дайте мне знать. Если кто-то считает, что это проблема SSL/TLS, и знает, где я могу найти правильные версии для 64-битной Windows/Windows Server 2019, я был бы очень признателен, если бы указал мне правильное направление.

void __fastcall TMainForm::SendBtnClick(TObject *Sender)
{
    try
    {
        // Point to SSL
        IdOpenSSLSetLibPath(L"C:\\openssl");

        // Set up SMTP
        TIdSMTP *IdSMTP = new TIdSMTP(NULL);
        IdSMTP->Host = "smtp.gmail.com";
        IdSMTP->Port = 587;
        IdSMTP->Username = "[email protected]";
        IdSMTP->Password = "my_password_phrase";
        IdSMTP->AuthType = satDefault;

        // Set up SSL/TLS
        TIdSSLIOHandlerSocketOpenSSL *IdSSL = new TIdSSLIOHandlerSocketOpenSSL(IdSMTP);
        IdSSL->SSLOptions->SSLVersions << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2;
        IdSSL->SSLOptions->Mode = sslmUnassigned;
        IdSSL->SSLOptions->VerifyMode = TIdSSLVerifyModeSet();
        IdSSL->SSLOptions->VerifyDepth = 0;
        IdSMTP->IOHandler = IdSSL;

        // Had to move the IdSMTP TLS property below the IOHandler as
        // it appears that in C++ you should have your IOHandler set
        // up before setting the IdSMPT property, or you will get,
        // "SSL IOHandler is required for this setting" exception.

        IdSMTP->UseTLS = utUseExplicitTLS;

        // Set up Message
        TIdMessage *IdMessage = new TIdMessage(NULL);
        IdMessage->From->Address = "[email protected]";
        IdMessage->Recipients->Add()->Address = "[email protected]";
        IdMessage->Subject = "Test Email";
        IdMessage->Body->Text = "Super Cool Test...";


        try {
            IdSMTP->Connect();
            IdSMTP->Send(IdMessage);
            IdSMTP->Disconnect();
        } catch (const Exception &e) {
            ErrorMemo->Lines->Add("[Connect Exception]: " + e.Message);
        }

        // Tidy up
        delete IdSMTP;
        delete IdSSL;
        delete IdMessage;

        ErrorMemo->Lines->Add("No leaks");
    }
    catch (const Exception &exception)
    {
        ErrorMemo->Lines->Add("[Method Exception]: " + exception.Message);
    }
}

Я попробовал использовать свойства компонента времени разработки и очень мало кода, который вы видите выше. Кроме того, я установил несколько различных двоичных версий пакетов SSL из результатов поиска, но, похоже, не смог заставить их работать.

Пожалуйста, не загружайте изображения кода/данных/ошибок.. В вашем случае это еще хуже, потому что у вас явно есть сообщения об ошибках в текстовой форме внутри TMemo, откуда их можно легко скопировать и вставить непосредственно в ваш вопрос.
Ken White 07.07.2024 21:56

Спасибо, Кен, за то, что указал мне на ссылку на политику, я понял и удалил изображения.

pjackson 08.07.2024 02:50

Вам следовало заменить их текстовыми сообщениями об ошибках, как я упоминал в своем предыдущем комментарии. Без этих сообщений ваш вопрос не так ясен.

Ken White 08.07.2024 02:53

Я просто переписал сообщение и написал сообщение об ошибке. Я также отредактировал содержание, чтобы оно было более понятным. Другое изображение не должно было быть прикреплено, как это описано в коде. Еще раз спасибо за ваши комментарии.

pjackson 08.07.2024 03:19
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
4
90
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

SSL/TLS и аутентификация — это две разные вещи. Рассматриваемая ошибка не имеет ничего общего с SSL/TLS.

Поскольку вы пытаетесь использовать имя пользователя + пароль, убедитесь, что ваша учетная запись Gmail настроена на это. Если у вас включена двухфакторная аутентификация, вам необходимо создать Пароль приложения вместо использования настоящего пароля. В противном случае вам придется включить «менее безопасные приложения», чтобы использовать ваш настоящий пароль.

Альтернативой является использование аутентификации OAuth, которую в настоящее время предпочитает Gmail.

В настоящее время Indy официально не поддерживает OAuth, однако в репозитории Indy GitHub доступна ветка sasl-oauth (она еще не объединена с основным кодом), которая добавляет новые TIdSASL... компоненты для OAuth. Например, TIdSASLXOAuth2 можно использовать с Gmail. Вы можете установить для свойства TIdSMTP::AuthType значение satSASL, а затем включить объект TIdSASLXOAuth2 в коллекцию TIdSMTP::SASLMechanisms, например:

__fastcall TMainForm::TMainForm(TComponent *Owner)
    : TForm(Owner)
{
    // Point to SSL
    IdOpenSSLSetLibPath(_D("C:\\openssl"));
}

void __fastcall TMainForm::SendBtnClick(TObject *Sender)
{
    try
    {
        String OAuthToken = ...; // see further below...

        // Set up SMTP
        TIdSMTP *IdSMTP = new TIdSMTP(NULL);
        IdSMTP->Host = _D("smtp.gmail.com");
        IdSMTP->Port = 587;
        IdSMTP->AuthType = satSASL;

        // Set up SSL/TLS
        TIdSSLIOHandlerSocketOpenSSL *IdSSL = new TIdSSLIOHandlerSocketOpenSSL(IdSMTP);
        IdSSL->SSLOptions->SSLVersions = TIdSSLVersions() << sslvTLSv1 << sslvTLSv1_1 << sslvTLSv1_2;
        IdSSL->SSLOptions->Mode = sslmUnassigned;
        IdSSL->SSLOptions->VerifyMode = TIdSSLVerifyModeSet();
        IdSSL->SSLOptions->VerifyDepth = 0;
        IdSMTP->IOHandler = IdSSL;

        // Had to move the IdSMTP TLS property below the IOHandler as
        // it appears that you should have your IOHandler set up
        // before setting the UseTLS property, or you will get a
        // "SSL IOHandler is required for this setting" exception.

        IdSMTP->UseTLS = utUseExplicitTLS;

        // Set up SASL
        TIdSASLXOAuth2 *IdOAuth = new TIdSASLXOAuth2(IdSMTP);
        IdOAuth->Username = _D("[email protected]");
        IdOAuth->Password = OAuthToken;
        IdSMTP->SASLMechanisms->Add()->SASL = IdOAuth;

        // Set up Message
        TIdMessage *IdMessage = new TIdMessage(IdSMTP);
        IdMessage->From->Address = _D("[email protected]");
        IdMessage->Recipients->Add()->Address = _D("[email protected]");
        IdMessage->Subject = _D("Test Email");
        IdMessage->Body->Text = _D("Super Cool Test...");

        try {
            IdSMTP->Connect();
            try {
                IdSMTP->Send(IdMessage);
            } __finally {
                IdSMTP->Disconnect();
            }
        } catch (const Exception &e) {
            ErrorMemo->Lines->Add("[SMTP Exception]: " + e.Message);
        }

        // Tidy up
        delete IdSMTP;

        ErrorMemo->Lines->Add("No leaks");
    }
    catch (const Exception &exception)
    {
        ErrorMemo->Lines->Add("[Method Exception]: " + exception.Message);
    }
}

Вам просто придется вручную получить токен доступа OAuth из Gmail, например, через HTTP (более подробную информацию см. в разделе Использование OAuth 2.0 для доступа к API Google), а затем вы сможете назначить этот токен свойству TIdSASLXOAuth2::Password, как показано выше. Или вместо этого вы можете вернуть его из события TIdSASLXOAuth2::OnGetAccessToken, например:

void __fastcall TMainForm::GetAccessToken(TObject* Sender, String &AccessToken)
{
    AccessToken = ...;
}

...

// Set up SASL
TIdSASLXOAuth2 *IdOAuth = new TIdSASLXOAuth2(IdSMTP);
IdOAuth->Username = _D("[email protected]");
IdOAuth->OnGetAccessToken = &GetAccessToken;

При этом, если вы не хотите устанавливать код филиала, вы можете просто использовать метод TIdSMTP::SendCmd(), чтобы вручную отправить необходимую AUTH XOAUTH2 SMTP-команду (подробности см. в Механизм OAuth 2.0) перед вызовом TIdSMTP::Send() (только обязательно заранее установите TIdSMTP::AuthType на satNone), например:

IdSMTP->Connect();
try {
    String user = _D("[email protected]");
    IdSMTP->SendCmd(_D("AUTH XOAUTH2 ") + TIdEncoderMIME::EncodeString(_D("user = ") + user + _D("\x01auth=Bearer ") + OAuthToken + _D("\x01\x01")), 235);

    IdSMTP->Send(IdMessage);
} __finally {
    IdSMTP->Disconnect();
}

Прочитав новые требования Google к использованию сторонних приложений с GSuite, они заявили, что к осени 2024 года OAuth будет требоваться для всего стороннего доступа, поэтому имя пользователя + пароль, а также менее безопасные приложения больше не будут доступны. Ссылку оставлю во втором комментарии, она здесь слишком длинная. Прочитав документацию об обновлении Indy до более новой ветки, они упоминают о возможности взлома EMSEdge Обновление Indy. Поэтому я попробую маршрут TIdSMTP::SendCmd(). Спасибо.

pjackson 09.07.2024 16:54

Ссылка Google про OAuth Переход с менее безопасных приложений на OAuth

pjackson 09.07.2024 16:55

Если вы прочитаете объявление Google еще раз более внимательно, то увидите, что исчезнут только «менее безопасные приложения». Пароли приложений по-прежнему будут работать, а это означает, что имя пользователя + пароль по-прежнему будут работать. Вы просто не можете использовать свой настоящий пароль, вам придется генерировать пароль в настройках Gmail для каждого экземпляра приложения. Я уже давно использую пароли приложений с Indy и Gmail, и все работает нормально. Но да, в конечном итоге код OAuth SASL также будет объединен с основным кодом Indy.

Remy Lebeau 09.07.2024 17:00

Я заметил опечатку в примере с SendCmd, исправил ее.

Remy Lebeau 09.07.2024 17:13

Спасибо. И то, что вы сказали, очень приятно знать. Да, я прочитаю документацию еще раз. Меня это не все связало.

pjackson 09.07.2024 17:18

наконец-то получилось с вашими комментариями.

pjackson 09.07.2024 21:38

Другие вопросы по теме