0
votes

I'm trying to use Mailkit to get emails from my Gmail:

private readonly string[] Scopes = { GmailService.Scope.GmailReadonly };
    private UserCredential GetGmailCredential()
    {
        UserCredential credential;
        using (var stream = new FileStream("client_id.json", FileMode.Open, FileAccess.Read))
        {
            // The file token.json stores the user's access and refresh tokens, and is created
            // automatically when the authorization flow completes for the first time.
            string credPath = "token.json";
            credential = GoogleWebAuthorizationBroker.AuthorizeAsync(
                GoogleClientSecrets.Load(stream).Secrets,
                Scopes,
                "user",
                CancellationToken.None,
                new FileDataStore(credPath, true),
                new LocalServerCodeReceiver()).Result;
            Console.WriteLine($"Credential file saved to: {credPath}");
        }

        return credential;
    }

    [HttpGet("check")]
    public string GetD()
    {
        using (var client = new ImapClient())
        {
            client.Connect("imap.gmail.com", 993, SecureSocketOptions.SslOnConnect);
            UserCredential credential = GetGmailCredential();
            //var oauth2 = new SaslMechanismOAuth2("myaccount008@gmail.com", credential.Token.AccessToken);
            var oauth2 = new SaslMechanismOAuth2("myaccount008",credential.Token.AccessToken);
            client.Authenticate(oauth2);
            client.Inbox.Open(FolderAccess.ReadOnly);

            var uids = client.Inbox.Search(SearchQuery.FromContains("bill"));
            string subjects = string.Empty;

            foreach (var uid in uids)
            {
                var message = client.Inbox.GetMessage(uid);
                subjects += message.Subject + Environment.NewLine;
                // write the message to a file
                message.WriteTo(string.Format("{0}.eml", uid));
            }

            client.Disconnect(true);
            return subjects;
        }
    }

Line: client.Authenticate(oauth2); throw exception ->

enter image description here

MailKit.Security.AuthenticationException

I followed this answer to setup my Gmail API, but then I got the Authentication Failed issue.

Update 1

Here is the log:

Connected to imaps://imap.gmail.com:993/ S: * OK Gimap ready for
requests from 122.199.45.135 d3mb920463124pjs C: A00000000 CAPABILITY
S: * CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA ID XLIST
CHILDREN X-GM-EXT-1 XYZZY SASL-IR AUTH=XOAUTH2 AUTH=PLAIN
AUTH=PLAIN-CLIENTTOKEN AUTH=OAUTHBEARER AUTH=XOAUTH S: A00000000 OK
Thats all she wrote! d3mb920463124pjs C: A00000001 AUTHENTICATE
XOAUTH2
dXNlcj1mcmFudmEwMDhAZ21haWwuY29tAWF1dGg9QmVhcmVyIHlhMjkuYTBBZHcxeGVWZDNSWWwzcVZlblZwVm1MbDBRRVVyWkdxd05veEd0QWpcLVhHNjRBaF90eWM0NWhwSFprZHA0d3dPWlpGMVZwbGM3dGo1Tm80eVMwc2lPNE1VYmhHV1I1WE9sdWtLUGY4TF9QU0dpZjhrSWM2UXNUbTQwYjlweDNBeXE4bVYtOTM2akEtSHdXekNQVFdGMGk0NGozX2FOTmhEYk5rAQE=
S: +
eyJzdGF0dXMiOiI0MDAiLCJzY2hlbWVzIjoPQmVhcmVyIiwic2NvcGUiOiJodHRwczovL21haWwuZ29vZ2xlLmNvbS8ifQ==
C:  S: A00000001 NO [AUTHENTICATIONFAILED] Invalid credentials
(Failure)

The scope, I use: GmailService.Scope.GmailReadonly as I only need to retrieve emails.

Update 2

Updated my scope to:

private readonly string[] Scopes = { GmailService.Scope.MailGoogleCom };
Connected to imaps://imap.gmail.com:993/
S: * OK Gimap ready for requests from 122.199.45.135 s90mb321411106pjc
C: A00000000 CAPABILITY.
S: * CAPABILITY IMAP4rev1 UNSELECT IDLE NAMESPACE QUOTA ID XLIST CHILDREN X-GM-EXT-1 XYZZY SASL-IR AUTH=XOAUTH2 AUTH=PLAIN AUTH=PLAIN-CLIENTTOKEN AUTH=OAUTHBEARER AUTH=XOAUTH
S: A00000000 OK Thats all she wrote! s90mb321411106pjc
C: A00000001 AUTHENTICATE XOAUTH2 dXNlcj1mcmFudmEwMDhAZ21haWwuY29tAWF1dGg9QmVhcmVyIHlhMjkuYTBBZHcxeGVWZDNSWWwzcVZlblZwVm1MbDBRRVVyWkdxd05veEd0QWpXLVhHNjRBaF90eWM0NWhwSFprZHA0d3dPWlpGMVZwbGM3dGo1Tm80eVMwc2lPNE1VYmhHV1I1WE9sdWtLUGY4TF9QU0dpZjhrSWM2UXNUbTQwYjlweDNBeXE4bVYtOTM2akEtSHdXekNQVFdGMGk0NGozX2FPTmhEYk5rAQE=
S: +
eyJzdGF0dXMiOiI0MDAiLCJzY2hlbWVzIjoiSmVhcmVyIiwic2NvcGUiOiJodHRwczovL21haWwuZ29vZ2xlLmNvbS8ifQ==
C: 
S: A00000001 NO [AUTHENTICATIONFAILED] Invalid credentials (Failure)

Update 3

I keep everything same and also tried to use GmailService.Scope.ReadOnly then replaced ImapClient with the following code, now it works.

      // Create Gmail API service.
        service = new GmailService(new BaseClientService.Initializer()
        {
            HttpClientInitializer = credential,
            ApplicationName = ApplicationName,
        });

This piece of code is from the Gmail Api. It would be great if I could use Mailkit to do the authentication.

1
9 times out of 10, the problem is because you are using the wrong scope(s) and/or because you are using the wrong username string in the AuthorizeAsync() method. Also new SaslMechanismOAuth2("myaccount008",credential.Token.AccessToken); makes it look like you are not adding the @gmail.com to the account name and you need to do that. Get a protocol log and base64 decode the error message that the server returns when it fails. - jstedfast
hi @jstedfast thanks for your help, I have also tried the account with "at gmail", still doesn't work. I also included my Scope. As to the log, how to do a protocol log?? I have been stuck here for hours, and my brain has stopped work >_<; - Franva
new ImapClient (new ProtocolLogger ("imap.log")) will dump a protocol log to imap.log or whatever file name you use. "Also included my Scope" - what does that mean? What is the scope value? That's what I need to see. - jstedfast
This is the scope you need: Scopes = new string[] { GmailService.Scope.MailGoogleCom } - jstedfast
thanks @halfer suggestion accepted. - Franva

1 Answers

2
votes

Part of the problem is that you are using the wrong scope. You need to use GoogleService.Scope.MailGoogleCom.

The scope you are using is not for IMAP or POP3 access, it only works for Google’s web request API.

The following code works for me:

const string GMailAccount = "username@gmail.com";

var clientSecrets = new ClientSecrets {
    ClientId = "XXX.apps.googleusercontent.com",
    ClientSecret = "XXX"
};

var codeFlow = new GoogleAuthorizationCodeFlow (new GoogleAuthorizationCodeFlow.Initializer {
    DataStore = new FileDataStore ("CredentialCacheFolder", false),
    Scopes = new [] { "https://mail.google.com/" },
    ClientSecrets = clientSecrets
});

var codeReceiver = new LocalServerCodeReceiver ();
var authCode = new AuthorizationCodeInstalledApp (codeFlow, codeReceiver);
var credential = await authCode.AuthorizeAsync (GMailAccount, CancellationToken.None);

if (authCode.ShouldRequestAuthorizationCode (credential.Token))
    await credential.RefreshTokenAsync (CancellationToken.None);

var oauth2 = new SaslMechanismOAuth2 (credential.UserId, credential.Token.AccessToken);

using (var client = new ImapClient ()) {
    await client.ConnectAsync ("imap.gmail.com", 993, SecureSocketOptions.SslOnConnect);
    await client.AuthenticateAsync (oauth2);
    await client.DisconnectAsync (true);
}