Как отправить электронное письмо в .Net в соответствии с новыми политиками безопасности?

Чтобы лучше защитить своих пользователей, почтовые провайдеры GMail и другие рекомендуют обновлять все наши приложения до OAuth 2.0.

Я прав, что это означает, что System.Net.Mail больше не работает, и нам нужно использовать другую библиотеку, например MailKit?

В общем, я пытаюсь понять, как отправить электронное письмо без разрешения "Доступ для менее безопасных приложений"?

Потому что у меня System.Net.Mail.SmtpException: The SMTP server requires a secure connection or the client was not authenticated. The server response was: 5.5.1 Authentication Required. Выполняется smtpClient.Send(message);.

Если единственный способ решить эту проблему - использовать MailKit, я думаю, что этот вопрос будет хорошим практическим пошаговым руководством по переключению от System.Net.Mail до использования MailKit и Google.Apis.Auth.OAuth2. Я не знаю, может быть, общее решение будет использовать DotNetOpenAuth?

У меня есть следующий класс в моем приложении, который соответствует отправке письма на любой адрес (gmail, yandex и другие):

public class EmailSender
{
    public void SendEmail(SmtpServerSettings serverSettings, SendEmailRequest emailRequest)
    {
        // Usually I have 587 port, SmtpServerName = smtp.gmail.com 
        _logger.Trace("Sending message with subject '{0}' using SMTP server {1}:{2}",
                      emailRequest.Subject,
                      serverSettings.SmtpServerName,
                      serverSettings.SmtpPort);

        try
        {
            using (var smtpClient = new SmtpClient(serverSettings.SmtpServerName, (int)serverSettings.SmtpPort))
            {
                smtpClient.EnableSsl = serverSettings.SmtpUseSsl; // true
                if (!string.IsNullOrEmpty(serverSettings.UserName) || !string.IsNullOrEmpty(serverSettings.EncryptedPassword))
                {
                    smtpClient.Credentials = new NetworkCredential(serverSettings.UserName, serverSettings.EncryptedPassword);
                }

                smtpClient.DeliveryMethod = SmtpDeliveryMethod.Network;
                smtpClient.Timeout = (int)serverSettings.SmtpTimeout.TotalMilliseconds;

                using (var message = new MailMessage())
                {
                    message.From = new MailAddress(serverSettings.FromAddress);

                    emailRequest.To.ForEach(message.To.Add);
                    emailRequest.CC.ForEach(message.CC.Add);
                    emailRequest.Bcc.ForEach(message.Bcc.Add);

                    message.Subject = emailRequest.Subject.Replace('\r', ' ').Replace('\n', ' ');
                    message.Body = emailRequest.Body;
                    message.BodyEncoding = Encoding.UTF8;
                    message.IsBodyHtml = false;

                    smtpClient.Send(message);
                }
            }

            _logger.Trace("Sent message with subject '{0}' using SMTP server {1}:{2}",
                          emailRequest.Subject,
                          serverSettings.SmtpServerName,
                          serverSettings.SmtpPort);
        }
        catch (SmtpFailedRecipientsException e)
        {
            var failedRecipients = e.InnerExceptions.Select(x => x.FailedRecipient);
            LogAndReThrowWithValidMessage(e, EmailsLocalization.EmailDeliveryFailed, failedRecipients);
        }
   }
}

Он отлично работает до новых политик безопасности Google.

Я знаю, что System.Net.Mail не поддерживает OAuth2. Я решил использовать MailKit's SmtpClient для отправки сообщений.

После расследования я понимаю, что мой исходный код не так сильно изменяется, потому что API MailKit's выглядит очень похоже (с System.Net.Mail).

За исключением одной детали: мне нужен токен доступа пользователя OAuth (MailKit не имеет кода, который будет извлекать токен OAuth, но он может использовать его, если он у меня есть).

Итак, в будущем у меня будет следующая строка:

smtpClient.Authenticate (usersLoginName, usersOAuthToken);

У меня есть идея добавить GoogleCredentials в качестве нового параметра к методу SendEmail:

public void SendEmail(SmtpServerSettings serverSettings, SendEmailRequest emailRequest, 
                      GoogleCredentials credentials)
{
    var certificate = new X509Certificate2(credentials.CertificateFilePath,
                                           credentials.PrivateKey,
                                           X509KeyStorageFlags.Exportable);

     var credential = new ServiceAccountCredential(
                      new ServiceAccountCredential.Initializer(credentials.ServiceAccountEmail)
                             {
                                 Scopes = new[] { "https://mail.google.com/" },
                                 User = serverSettings.UserName
                             }.FromCertificate(certificate));

    ....
    //my previous code but with MailKit API
}

Как получить usersOAuthToken? Это лучший способ использования Google.Apis.Auth.OAuth2?

Код, который я написал выше, предназначен только для GMail и НЕ РАБОТАЕТ для yandex.ru или других почтовых провайдеров. Для работы с другими мне, вероятно, понадобится использовать другие библиотеки OAuth2. Но я не хочу иметь много механизмов аутентификации в моем коде для многих возможных почтовых провайдеров. Я хотел бы иметь ОДИН ОБЩИЙ РЕШЕНИЕ для каждого почтового провайдера. И одна библиотека, которая может отправлять электронную почту (например,.net smtpclient)

Ответы

Ответ 1

Общее решение https://galleryserverpro.com/use-gmail-as-your-smtp-server-even-when-using-2-factor-authentication-2-step-verification/

1) Используйте браузер для входа в свою учетную запись Google и перехода к настройкам входа и безопасности. Найдите двухэтапную аутентификацию.

2) Если двухэтапная аутентификация выключена, и вы хотите сохранить ее таким образом, чтобы это означало, что вам нужно будет реализовать много механизмов auth, как вы сказали.

Решение. Включите его, а затем создайте и используйте пароль приложения Google. Он должен работать! Вам не нужно использовать другие библиотеки, такие как mailkit.

Ответ 2

Как получить usersOAuthToken?

Первое, что вам нужно сделать, это следовать инструкциям Google для получения учетных данных OAuth 2.0 для вашего приложения.

Как только вы это сделали, самый простой способ получить токен доступа - использовать библиотеку Google Google.Apis.Auth:

using System;
using System.Threading;
using System.Security.Cryptography.X509Certificates;

using Google.Apis.Auth.OAuth2;

using MimeKit;
using MailKit.Net.Smtp;
using MailKit.Security;

namespace Example {
    class Program
    {
        public static async void Main (string[] args)
        {
            var certificate = new X509Certificate2 (@"C:\path\to\certificate.p12", "password", X509KeyStorageFlags.Exportable);
            var credential = new ServiceAccountCredential (new ServiceAccountCredential
                .Initializer ("[email protected]") {
                    // Note: other scopes can be found here: https://developers.google.com/gmail/api/auth/scopes
                    Scopes = new[] { "https://mail.google.com/" },
                    User = "[email protected]"
                }.FromCertificate (certificate));

            // Note: result will be true if the access token was received successfully
            bool result = await credential.RequestAccessTokenAsync (CancellationToken.None);

            if (!result) {
                Console.WriteLine ("Error fetching access token!");
                return;
            }

            var message = new MimeMessage ();
            message.From.Add (new MailboxAddress ("Your Name", "[email protected]"));
            message.To.Add (new MailboxAddress ("Recipient Name", "[email protected]"));
            message.Subject = "This is a test message";

            var builder = new BodyBuilder ();
            builder.TextBody = "This is the body of the message.";
            builder.Attachments.Add (@"C:\path\to\attachment");

            message.Body = builder.ToMessageBody ();

            using (var client = new SmtpClient ()) {
                client.Connect ("smtp.gmail.com", 587, SecureSocketOptions.StartTls);

                // use the access token as the password string
                client.Authenticate ("[email protected]", credential.Token.AccessToken);

                client.Send (message);

                client.Disconnect (true);
            }
        }
    }
}

Это лучший способ использовать Google.Apis.Auth.OAuth2?

Почему бы вам не использовать свой API для получения токена аутентификации? Кажется, лучший способ получить его мне...

Можно ли отправлять электронные письма другим учетным записям gmail?

Да, любое электронное письмо, отправленное через GMail, может быть отправлено на любой другой адрес электронной почты - это не должно быть только для других адресов GMail.

Ответ 3

Ошибки аутентификации возникают при использовании SMTP-сервера Gmail из приложений, не реализующих специфические требования безопасности Google. В настройках учетной записи Gmail включите:      "Вход и безопасность" >          "Связанные приложения и сайты" >              "Разрешить менее безопасные приложения" >                 О