2

I would like to read e-mails from a signed in user using IMAP.

I have created a console application for testing purposes and making sure the app-registration settings in azure are correct.

The console application is working as intended.

  1. A Microsoft login window is shown where the user can enter their credentials.
  2. An access token is received and is passed to MailKit in order to get the user's emails.

The problem

When I try to authenticate using MailKit in a MVC .net standard web-application, I get an error saying "Authentication failed".

However, when I copy the access-token I acquired using the console- application and use it in my web-application I do not get the authorization error and can successfully authenticate and read emails. (I use the access-token as second parameter in var oauth2 = new SaslMechanismOAuth2("[Email here]", oathToken.access_token);).

I have used DotNetOpenAuth in order to show a Microsoft login window. (I could not find a MSAL example for a web-application where i didn't have to add OWIN as middleware. I only want to authenticate in order to get emails, not for application wide authentication and authorization.)

Console application code (this works):

             // Using Microsoft.Identity.Client 4.22.0

             // Configure the MSAL client to get tokens
            var pcaOptions = new PublicClientApplicationOptions
             {
                 ClientId = "[client-id here]",
                 AadAuthorityAudience = AadAuthorityAudience.AzureAdMultipleOrgs,
             };

            var pca = PublicClientApplicationBuilder
                .CreateWithApplicationOptions(pcaOptions).Build();

            var scopes = new string[] {
                 "email",
                 "offline_access",
                 "https://outlook.office365.com/IMAP.AccessAsUser.All" };

            // Make the interactive token request
            var authResult = await pca.AcquireTokenInteractive(scopes).ExecuteAsync();

            var oauth2 = new SaslMechanismOAuth2(authResult.Account.Username, authResult.AccessToken);

            using (var client = new ImapClient())
            {
                await client.ConnectAsync("outlook.office365.com", 993, SecureSocketOptions.Auto);
                await client.AuthenticateAsync(oauth2);

                var inbox = client.Inbox;
                inbox.Open(FolderAccess.ReadOnly);

                for (int i = 0; i < inbox.Count; i++)
                {
                    var message = inbox.GetMessage(i);
                    Console.WriteLine("Subject: {0}", message.Subject);
                }

                await client.DisconnectAsync(true);
            }

Web-application (this doesn't work):

public ActionResult Index()
    {
        string clientID = "[client-id here]";
        string clientSecret = "[client-secret here]";
        string redirectUri = "[redirectUri here]";

        AuthorizationServerDescription server = new AuthorizationServerDescription
        {
            AuthorizationEndpoint = new Uri("https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize"),
            TokenEndpoint = new Uri("https://login.microsoftonline.com/organizations/oauth2/v2.0/token"),
            ProtocolVersion = ProtocolVersion.V20,
        };

        List<string> scopes = new List<string>
        {
           "email",
           "offline_access",
           "https://outlook.office365.com/IMAP.AccessAsUser.All"
        };

        WebServerClient consumer = new WebServerClient(server, clientID, clientSecret);

        OutgoingWebResponse response = consumer.PrepareRequestUserAuthorization(
        scopes, new Uri(redirectUri));

        return response.AsActionResultMvc5();
    }

    public async Task<ActionResult> Authorized(string code, string state, string session_state)
    {
        List<string> scopes = new List<string>
        {
            "IMAP.AccessAsUser.All",
            "User.Read",
            "offline_access"
        };

        HttpClient httpClient = new HttpClient();

        var values = new Dictionary<string, string>
        {
            { "Host", "https://login.microsoftonline.com" },
            { "Content-Type", "application/x-www-form-urlencoded" },
            { "client_id", "[client-id here]" },
            { "scope", string.Join(" ",scopes) },
            { "code", code },
            { "redirect_uri", [redirectUri here] },
            { "grant_type", "authorization_code" },
            { "client_secret", "[client-secret here]" },
            { "state", state },
        };

        var content = new FormUrlEncodedContent(values);

        var response = await httpClient.PostAsync("https://login.microsoftonline.com/organizations/oauth2/v2.0/token", content);


        var jsonString = await response.Content.ReadAsStringAsync();
        var oathToken = JsonConvert.DeserializeObject<OathToken>(jsonString);

        var oauth2 = new SaslMechanismOAuth2("[Email here]", oathToken.access_token);
        var stringBuilder = new StringBuilder();

        using (var client = new ImapClient())
        {
            try
            {
                await client.ConnectAsync("outlook.office365.com", 993, SecureSocketOptions.Auto);
                await client.AuthenticateAsync(oauth2);

                var inbox = client.Inbox;
                inbox.Open(FolderAccess.ReadOnly);

                for (int i = 0; i < inbox.Count; i++)
                {
                    var message = inbox.GetMessage(i);
                    stringBuilder.AppendLine($"Subject: {message.Subject}");
                }

                await client.DisconnectAsync(true);

                return Content(stringBuilder.ToString());

            }
            catch (Exception e)
            {
                return Content(e.Message);
            }

        }
    }

The problems occurs on this line: await client.AuthenticateAsync(oauth2); I receive an error saying "Authentication failed" However, when using the access-token from the console application in the web-application i do not get this error and can successfully authenticate and read emails in the web-application.

Can anyone point me in the right direction?

Thanks.

abc54
  • 63
  • 2
  • 6

0 Answers0