Andy Furniss
Andy Furniss

Reputation: 3904

Sending mail using MailKit with Gmail OAuth

I am trying to create an application that will send emails to customers when they make a purchase. We have our own GMail account which I will be using to send the emails from.

I have set up my application and created credentials in the Google API Console. I found this question on MailKit's GitHub which looked like it'd be an easy enough approach but it doesn't seem to be working for me.

Here's my code:

var secrets = new ClientSecrets
{
    ClientId = [CLIENTID]
    ClientSecret = [SECRET]
};

var googleCredentials = await GoogleWebAuthorizationBroker.AuthorizeAsync(secrets, new[] { GmailService.Scope.MailGoogleCom }, email, CancellationToken.None);
await googleCredentials.RefreshTokenAsync(CancellationToken.None);

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

    var credentials = new NetworkCredential(googleCredentials.UserId, googleCredentials.Token.AccessToken);
    client.Authenticate(credentials);

    await client.SendAsync(message);
    client.Disconnect(true);
}

The call to Authenticate gives the following error:

MailKit.Security.AuthenticationException : AuthenticationInvalidCredentials: 5.7.8 Username and Password not accepted. Learn more at 5.7.8 https://support.google.com/mail/?p=BadCredentials m3-v6sm3447324wrs.39 - gsmtp

The Google support page in the exception basically just says either use two-step verification + app passwords or enable less secure apps. I don't want either of these. Why is it so difficult to do this in .NET? I have done this with node before and it was incredibly simple:

var smtp = mailer.createTransport({
    service: "Gmail",
    auth: {
        type: "OAuth2",
        user: process.env.EMAIL,
        clientId: process.env.CLIENT_ID,
        clientSecret: process.env.CLIENT_SECRET,
        refreshToken: process.env.REFRESH_TOKEN
    }
});

Please not that I have already seen this answer but I don't really understand how I go about getting an X509 certificate that is trusted by Google. There's nothing related to this on Google's documentation as far as I can see.

Upvotes: 10

Views: 13279

Answers (2)

hubert17
hubert17

Reputation: 294

Send email in Gmail Mailkit via Refresh Token

    // https://console.developers.google.com
var secrets = new ClientSecrets
{
    ClientId = "",
    ClientSecret = ""
};
// Generating a refresh token - https://www.youtube.com/watch?v=hfWe1gPCnzc
var token = new TokenResponse { RefreshToken = "YourRefreshToken" };
var googleCredentials = new UserCredential(new GoogleAuthorizationCodeFlow(
    new GoogleAuthorizationCodeFlow.Initializer
    {
        ClientSecrets = secrets
    }), username, token);

emailClient.Connect("smtp.gmail.com", 587, SecureSocketOptions.StartTls);   
await googleCredentials.GetAccessTokenForRequestAsync();
var oauth2 = new SaslMechanismOAuth2(googleCredentials.UserId, googleCredentials.Token.AccessToken);
emailClient.Authenticate(oauth2);

await emailClient.SendAsync(message); // MimeMessage
emailClient.Disconnect(true);

Upvotes: 5

Andy Furniss
Andy Furniss

Reputation: 3904

The solution was to authenticate with SaslMechanismOAuth2 instead of NetworkCredential. I now have the following, which works:

var secrets = new ClientSecrets
{
    ClientId = Environment.GetEnvironmentVariable("GMailClientId"),
    ClientSecret = Environment.GetEnvironmentVariable("GMailClientSecret")
};

var googleCredentials = await GoogleWebAuthorizationBroker.AuthorizeAsync(secrets, new[] { GmailService.Scope.MailGoogleCom }, email, CancellationToken.None);
if (googleCredentials.Token.IsExpired(SystemClock.Default))
{
    await googleCredentials.RefreshTokenAsync(CancellationToken.None);
}

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

    var oauth2 = new SaslMechanismOAuth2(googleCredentials.UserId, googleCredentials.Token.AccessToken);
    client.Authenticate(oauth2);

    await client.SendAsync(message);
    client.Disconnect(true);
}

Upvotes: 14

Related Questions