Harish Kumar
Harish Kumar

Reputation: 21

Send MIME content to a particular user using Chilkat and Graph API access token

I am trying to send MIME content to a single user using Chilkat library. For sending mail I am using access token of Graph API client credentials. But getting authentication failure error in chilkat. Below is the sample code.

Calling from Main method:

string mime = System.IO.File.ReadAllText(@"\\Mac\Home\Downloads\12_01.eml");
GetFreshToken(tenantID, clientID, clientSecret); 
SendMailUsingChilkat(tenantID,clientID, clientSecret,mime,"[email protected]");

Methods used in:

    bool GetFreshToken(string tenantId, string clientId, string clientSecret)
    {
        bool ret = false;
        Chilkat.OAuth2 oauth2 = new Chilkat.OAuth2();
        bool success;

        oauth2.ListenPort = 3017;

        oauth2.AuthorizationEndpoint = "https://login.microsoftonline.com/xxxxxx-xxxx-xxxx-xxxx-xxxx0f78bbd/oauth2/v2.0/authorize";
        oauth2.TokenEndpoint = "https://login.microsoftonline.com/xxxxxx-xxxx-xxxx-xxxx-xxxx0f78bbd/oauth2/v2.0/token";

        oauth2.ClientId = clientId;
        oauth2.ClientSecret = clientSecret;

        oauth2.CodeChallenge = false;
        
        oauth2.Scope = "openid profile offline_access https://outlook.office365.com/SMTP.Send https://outlook.office365.com/POP.AccessAsUser.All https://outlook.office365.com/IMAP.AccessAsUser.All";

        string url = oauth2.StartAuth();
        if (oauth2.LastMethodSuccess != true)
        {
            MessageBox.Show(oauth2.LastErrorText);
            return ret;
        }

        int numMsWaited = 0;
        while ((numMsWaited < 30000) && (oauth2.AuthFlowState < 3))
        {
            oauth2.SleepMs(100);
            numMsWaited = numMsWaited + 100;
        }

        if (oauth2.AuthFlowState < 3)
        {
            oauth2.Cancel();
            MessageBox.Show("No response from the browser!");
            return ret;
        }

        if (oauth2.AuthFlowState == 5)
        {
            MessageBox.Show("OAuth2 failed to complete.");
            MessageBox.Show(oauth2.FailureInfo);
            return ret;
        }

        if (oauth2.AuthFlowState == 4)
        {
            MessageBox.Show("OAuth2 authorization was denied.");
            MessageBox.Show(oauth2.AccessTokenResponse);
            return ret;
        }

        if (oauth2.AuthFlowState != 3)
        {
            MessageBox.Show("Unexpected AuthFlowState:" + Convert.ToString(oauth2.AuthFlowState));
            return ret;
        }

        Chilkat.JsonObject json = new Chilkat.JsonObject();
        json.Load(oauth2.AccessTokenResponse);
        json.EmitCompact = false;

        if (json.HasMember("expires_on") != true)
        {
            Chilkat.CkDateTime dtExpire = new Chilkat.CkDateTime();
            dtExpire.SetFromCurrentSystemTime();
            dtExpire.AddSeconds(json.IntOf("expires_in"));
            json.AppendString("expires_on", dtExpire.GetAsUnixTimeStr(false));
        }
        json.Emit();

        Chilkat.FileAccess fac = new Chilkat.FileAccess();
        ret = fac.WriteEntireTextFile("microsoftGraph.json", json.Emit(), "utf-8", false);
        return ret;
    }


    bool GetOrRefreshToken(string clientId, string clientSecret, out string accessToken)
    {
        bool ret = false;
        accessToken = null;
        Chilkat.JsonObject json = new Chilkat.JsonObject();
        bool success = json.LoadFile("microsoftGraph.json");
        if (success != true)
        {
            return false;
        }

        Chilkat.CkDateTime dtExpire = new Chilkat.CkDateTime();
        dtExpire.SetFromUnixTime(false, json.IntOf("expires_on"));

        if (dtExpire.ExpiresWithin(10, "minutes") != true)
        {
            Debug.WriteLine("No need to refresh, the access token won't expire within the next 10 minutes.");
            accessToken = json.StringOf("access_token");
            return true;
        }

        // OK, we need to refresh the access token by sending a POST like this:
       
        Chilkat.OAuth2 oauth2 = new Chilkat.OAuth2();

        oauth2.TokenEndpoint = "https://login.microsoftonline.com/xxxxxx-xxxx-xxxx-xxxx-xxxx0f78bbd/oauth2/v2.0/token";

        oauth2.ClientId = clientId;
        oauth2.ClientSecret = clientSecret;
        oauth2.RefreshToken = json.StringOf("refresh_token");
        success = oauth2.RefreshAccessToken();

        if (success != true)
        {
            Debug.WriteLine(oauth2.LastErrorText);
            return false;
        }

        // Update the JSON with the new tokens.
        json.UpdateString("access_token", oauth2.AccessToken);
        json.UpdateString("refresh_token", oauth2.RefreshToken);
       
        json.EmitCompact = false;
        Debug.WriteLine(json.Emit());

        if (json.HasMember("expires_on") != true)
        {
            dtExpire.SetFromCurrentSystemTime();
            dtExpire.AddSeconds(json.IntOf("expires_in"));
            json.AppendString("expires_on", dtExpire.GetAsUnixTimeStr(false));
        }

        Chilkat.FileAccess fac = new Chilkat.FileAccess();
        ret = fac.WriteEntireTextFile("microsoftGraph.json", json.Emit(), "utf-8", false);
        accessToken = json.StringOf("access_token");
        return ret;
    }

    async void SendMailUsingChilkat(string tenantId, string clientId, string clientSecret, string mime, string recipient) 
    {
        string accessToken;
        GetOrRefreshToken(clientId, clientSecret, out accessToken);

        Chilkat.Email email = new Chilkat.Email();
        email.LoadEml(mime);

        Chilkat.MailMan mailman = new Chilkat.MailMan();

        mailman.SmtpHost = "smtp.office365.com";
        mailman.SmtpPort = 587;
        mailman.StartTLS = true;

        mailman.SmtpUsername = email.From;
        mailman.OAuth2AccessToken = accessToken;

        bool success = mailman.SendMime(email.From, recipient, mime);

        if (success == true)
        {
            Debug.WriteLine("Mail Sent!");
            return;
        }

        if (mailman.LastSmtpStatus != 535)
        {
            Debug.WriteLine(mailman.LastErrorText);
            return;
        }

    }

I referred below links for implementation

https://www.example-code.com/csharp/office365_oauth2_access_token.asp
https://www.example-code.com/csharp/office365_refresh_access_token.asp
https://www.example-code.com/csharp/office365_smtp_send_email.asp

Upvotes: 2

Views: 222

Answers (1)

Chilkat Software
Chilkat Software

Reputation: 1624

At the time I'm writing this response, Chilkat has not yet been able to get the client credentials flow working with Office365. We can get an access token, but it fails to authenticate. I believe additional magical incantations need to be performed in Azure, and I've yet to discover the secret sauce.

Especially with different identity and account types: https://learn.microsoft.com/en-us/security/zero-trust/develop/identity-supported-account-types

See related Microsoft docs:

https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow

https://learn.microsoft.com/en-us/answers/questions/1046512/smtp-for-o365-with-client-credentials-flow.html

Upvotes: 0

Related Questions