How to send an email in .Net according to new security policies?
To better protect your users, GMail and others mail providers recommends to upgrade all of our applications to OAuth 2.0.
Am I right that this means that System.Net.Mail
don't work anymore and we need to use another library like MailKit
?
In general I'm trying to understand how to send an email without allowing "Access for less secure apps"?
Because I have 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.
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.
When smtpClient.Send(message);
executed.
If the only way to solve this problem is using MailKit
, I think that this question will be a good practical step-by-step switching tutorial from System.Net.Mail
to using MailKit
and Google.Apis.Auth.OAuth2
. I don't know maybe general solution will be using DotNetOpenAuth
?
I have the following class in my application that correspond to send an email to any address(gmail, yandex and others):
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);
}
}
}
It works fine until the new Google security policies.
I know that System.Net.Mail
does not support OAuth2
. I decided to use MailKit's
SmtpClient
to send messages.
After the investigation I understand that my initial code not change so much, because MailKit's
API looks very similar(with System.Net.Mail
).
Except one detail: I need to have the user's OAuth access token (MailKit does not have code that will fetch the OAuth token, but it can use it if I have it).
So in the future I will have the following line:
smtpClient.Authenticate (usersLoginName, usersOAuthToken);
I have an idea to add GoogleCredentials
as new parameter to the SendEmail
method:
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
}
How to get usersOAuthToken
? Is it the best practice technique to use Google.Apis.Auth.OAuth2
?
The code I posted above is for GMail ONLY and WILL NOT work for yandex.ru or other mail providers. To work with others, I will probably need to use another OAuth2 librarys. But I don't want to have many authentication mechanisms in my code for many possible mail providers. I would like to have ONE GENERAL SOLUTION for every mail providers. And one library that can send email (like .net smtpclient did)
The general solution is https://galleryserverpro.com/use-gmail-as-your-smtp-server-even-when-using-2-factor-authentication-2-step-verification/
1) Use a browser to log in to your Google account and go to your Sign-in & security settings. Look for the 2-step verification setting.
2) If 2-step verification is off and you want to keep it that way that's mean that you will need to implement many auth mechanism as you said.
Solution: Turn it on and then generate and use google app password. It should work! You don't need to use other libraries like mailkit
.
How to get usersOAuthToken?
The first thing you need to do is follow Google's instructions for obtaining OAuth 2.0 credentials for your application.
Once you've done that, the easiest way to obtain an access token is to use Google's Google.Apis.Auth library:
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:pathtocertificate.p12", "password", X509KeyStorageFlags.Exportable);
var credential = new ServiceAccountCredential (new ServiceAccountCredential
.Initializer ("your-developer-id@developer.gserviceaccount.com") {
// Note: other scopes can be found here: https://developers.google.com/gmail/api/auth/scopes
Scopes = new[] { "https://mail.google.com/" },
User = "username@gmail.com"
}.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", "username@gmail.com"));
message.To.Add (new MailboxAddress ("Recipient's Name", "recipient@yahoo.com"));
message.Subject = "This is a test message";
var builder = new BodyBuilder ();
builder.TextBody = "This is the body of the message.";
builder.Attachments.Add (@"C:pathtoattachment");
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 ("username@gmail.com", credential.Token.AccessToken);
client.Send (message);
client.Disconnect (true);
}
}
}
}
Is it the best practice technique to use Google.Apis.Auth.OAuth2?
Why wouldn't you use their API for getting an authentication token? It seems like the best way to get it to me...
Will I can send an emails to other not gmail accounts?
Yes, any email you send through GMail can be sent to any other email address - it doesn't have to only be to other GMail addresses.
Authentication errors happen when using the Gmail smtp server from applications not implementing Google's specific security requirements. In the Gmail account settings, turn on: "Sign-in & security" > "Connected apps & sites" > “Allow less secure apps” > On
链接地址: http://www.djcxy.com/p/50528.html