MailKit and OAuth2 for gmail SMTP - 5.7.8 BadCredentials error

294 views Asked by At

I'm trying to use mailkit to send emails via a gmail account. I keep getting the 5.7.8 BadCredentials error.

My Oauth2 credentials in google specify a project name of "EmailTesting" for a desktop app - however I'm not specifying this string anywhere in my code. Is this a problem? The credentials.json file specified in the code is directly downloaded from the oauth2 credentials page.

UPDATE: Now using SaslMechanismOAuthBearer method since GMAIL indicates OAuthBearer. Also showing smtp.log at end.

My code is as follows:

    public async Task Test4()
    {
        UserCredential creds;
        using (var stream = new FileStream("credentials.json", FileMode.Open, FileAccess.Read))
        {
            creds = await GoogleWebAuthorizationBroker.AuthorizeAsync(
                GoogleClientSecrets.FromStream(stream).Secrets,
                new[] { GmailService.Scope.GmailSend },
                "donotreply@INSERT_DOMAIN_HERE",
                CancellationToken.None);
            if (creds.Token.IsExpired(SystemClock.Default))
            {
                await creds.RefreshTokenAsync(CancellationToken.None);
            }
        }

        MailKit.Net.Smtp.SmtpClient client;
        client = new MailKit.Net.Smtp.SmtpClient();
        client.Connect("smtp.gmail.com", 465, MailKit.Security.SecureSocketOptions.SslOnConnect);
        client.Authenticate(new SaslMechanismOAuthBearer(creds.UserId, creds.Token.AccessToken));
        var builder = new MimeKit.BodyBuilder();

        builder.TextBody = "This is the body of the email";
        MimeMessage mail = new MimeKit.MimeMessage();
        mail.From.Add(MailboxAddress.Parse("donotreply@INSERT_DOMAIN_HERE"));
        mail.To.Add(MailboxAddress.Parse("erict@INSERT_DOMAIN_HERE"));
        mail.Subject = "this is the subject";
        mail.Body = builder.ToMessageBody();
        await client.SendAsync(mail);
        client.Disconnect(true);
        client.Dispose();
        mail.Dispose();
    }

The first day I ran this, it redirected to a web page so that donotreply@INSERT_DOMAIN_HERE could give permission for the app to send emails. However, after that and since then, it would throw a 5.7.8 Username and Password not accepted error. All day yesterday the google 5.7.8 learn more link suggested I need to add a recovery phone - even though I had done that more than 24h ago, and verified. As of this morning, google does not add that suggestion anymore, but still throws a 5.7.8 error. Here is a log output:

S: 220 smtp.gmail.com ESMTP r12-20020a05621410cc00b0066d132b1c8bsm4912733qvs.102 - gsmtp
C: EHLO New-Laptop
S: 250-smtp.gmail.com at your service, [<IP-ADDRESS OF MY MACHINE>]
S: 250-SIZE 35882577
S: 250-8BITMIME
S: 250-STARTTLS
S: 250-ENHANCEDSTATUSCODES
S: 250-PIPELINING
S: 250-CHUNKING
S: 250 SMTPUTF8
C: STARTTLS
S: 220 2.0.0 Ready to start TLS
C: EHLO New-Laptop
S: 250-smtp.gmail.com at your service, [<IP-ADDRESS OF MY MACHINE>]
S: 250-SIZE 35882577
S: 250-8BITMIME
S: 250-AUTH LOGIN PLAIN XOAUTH2 PLAIN-CLIENTTOKEN OAUTHBEARER XOAUTH
S: 250-ENHANCEDSTATUSCODES
S: 250-PIPELINING
S: 250-CHUNKING
S: 250 SMTPUTF8
C: AUTH OAUTHBEARER ********
S: 334 eyJzdGF0dXMiOiJpbnZhbGlkX3JlcXVlc3QiLCJzY29wZSI6Imh0dHBzOi8vbWFpbC5nb29nbGUuY29tLyJ9
C: ********
S: 535-5.7.8 Username and Password not accepted. Learn more at...
2

There are 2 answers

3
Linda Lawton - DaImTo On BEST ANSWER

First you need to use the "https://mail.google.com/" to use the SMTP server second AuthenticateAsync should be using SaslMechanismOAuth2

using Google.Apis.Auth.OAuth2;
using Google.Apis.Util.Store;
using MailKit.Net.Smtp;
using MailKit.Security;
using MimeKit;

var to = "[email protected]";

// TODO: figure out how to get the users email back from the smtp server without having to request the profile scope. 
var from = "[email protected]";

var path = @"C:\Development\FreeLance\GoogleSamples\Credentials\Credentials.json";
var scopes = new[] { "https://mail.google.com/" };   // You can only use this scope to connect to the smtp server.



var credential = GoogleWebAuthorizationBroker.AuthorizeAsync(GoogleClientSecrets.FromFile(path).Secrets,
    scopes,
    "GmailSmtpUser",
    CancellationToken.None,
    new FileDataStore(Directory.GetCurrentDirectory(), true)).Result;

var message = new EmailMessage()
{
    From = from,
    To = to,
    MessageText = "This is a test message using https://developers.google.com/gmail/imap/xoauth2-protocol",
    Subject = "Testing GmailSMTP with XOauth2"
};

try
{
    using (var client = new SmtpClient())
    {
        client.Connect("smtp.gmail.com", 465, true);
        
        var oauth2 = new SaslMechanismOAuth2 (message.From, credential.Token.AccessToken);
        await client.AuthenticateAsync (oauth2, CancellationToken.None);
        
        client.Send(message.GetMessage());
        client.Disconnect(true);
    }

   
}
catch (Exception ex)
{
   Console.WriteLine(ex.Message);
}


public class EmailMessage
{
    public string To { get; set; }
    public string From { get; set; }
    public string Subject { get; set; }
    public string MessageText { get; set; }

    public MimeMessage GetMessage()
    {
        var body = MessageText;
        var message = new MimeMessage();
        message.From.Add(new MailboxAddress("From a user", From));
        message.To.Add(new MailboxAddress("To a user", To));
        message.Subject = Subject;
        message.Body = new TextPart("plain") { Text = body };
        return message;
    }
}
0
bmi On

Where is error in this code? Code ends with exception in command 'client.Authenticate(accessToken);' - 'MailKit.Security.AuthenticationException: '535: 5.7.3 Authentication unsuccessful [VI1PR04CA0132.eurprd04.prod.outlook.com'

            var scopes = new string[]
        {
            "https://outlook.office.com/.default"
        };

        var scopesStr = String.Join(" ", scopes.Select(x => x?.Trim()).Where(x => !String.IsNullOrEmpty(x)));
        var content = new FormUrlEncodedContent(new List<KeyValuePair<string, string>>
        {
            new KeyValuePair<string, string>("grant_type", "client_credentials"),
            //new KeyValuePair<string, string>("username", Username),
            //new KeyValuePair<string, string>("password", Password),
            new KeyValuePair<string, string>("client_id", ClientId),
            new KeyValuePair<string, string>("client_secret", ClientSecret),
            new KeyValuePair<string, string>("scope", scopesStr),
        });
        SaslMechanism accessToken;
        using (var client = new HttpClient())
        {
            var response = await client.PostAsync($"https://login.microsoftonline.com/{TenantId}/oauth2/v2.0/token", content).ConfigureAwait(continueOnCapturedContext: false);
            var responseString = await response.Content.ReadAsStringAsync();
            var json = JObject.Parse(responseString);
            var token = json["access_token"];
            accessToken = token != null
                ? new SaslMechanismOAuth2(Username, token.ToString())
                : null;
        }


        using (var client = new SmtpClient(/*new MailKit.ProtocolLogger(Console.OpenStandardOutput())*/))
        {
            try
            {
                client.Connect(SmtpHost, 587, SecureSocketOptions.Auto);  // "smtp.office365.com"
                client.Authenticate(accessToken);

                var email = new MimeMessage();
                email.From.Add(MailboxAddress.Parse(Username));
                email.To.Add(MailboxAddress.Parse(Username));
                email.Subject = "SMTP Test";
                email.Body = new TextPart("plain") { Text = "This is a test" };
                client.Send(email);
            }
            catch (Exception e)
            {
                Console.Error.WriteLine($"Error in 'send email': {e.GetType().Name} {e.Message}");
            }
        }