{"id":5649,"date":"2020-06-23T18:24:21","date_gmt":"2020-06-23T16:24:21","guid":{"rendered":"https:\/\/www.limilabs.com\/blog\/?p=5649"},"modified":"2023-08-29T09:20:09","modified_gmt":"2023-08-29T07:20:09","slug":"oauth2-office365-exchange-imap-pop3-smtp","status":"publish","type":"post","link":"https:\/\/www.limilabs.com\/blog\/oauth2-office365-exchange-imap-pop3-smtp","title":{"rendered":"OAuth 2.0 with Office365\/Exchange IMAP\/POP3\/SMTP"},"content":{"rendered":"\n<div class=\"well\">\nIn this series:\n<p>&nbsp;<\/p>\n<ul>\n<li>OAuth 2.0 with Office365\/Exchange IMAP\/POP3\/SMTP<\/li>\n\n<li><a href=\"https:\/\/www.limilabs.com\/blog\/oauth2-web-flow-office365-exchange-imap-pop3-smtp\">OAuth 2.0 web flow with Office365\/Exchange IMAP\/POP3\/SMTP<\/a><\/li>\n\n<li><a href=\"https:\/\/www.limilabs.com\/blog\/oauth2-password-grant-office365-exchange-imap-pop3-smtp\">OAuth 2.0 password grant with Office365\/Exchange IMAP\/POP3\/SMTP<\/a><\/li>\n\n<li><a href=\"https:\/\/www.limilabs.com\/blog\/oauth2-device-flow-office365-exchange-imap-pop3-smtp\">OAuth 2.0 device flow with Office365\/Exchange IMAP\/POP3\/SMTP<\/a><\/li>\n\n<li><a href=\"oauth2-client-credential-flow-office365-exchange-imap-pop3-smtp\">OAuth 2.0 client credential flow with Office365\/Exchange IMAP\/POP3\/SMTP<\/a><\/li>\n\n<\/ul><\/div>\n\n\n\n<p>This article shows how to implement OAuth 2.0 desktop flow to access Office365 via IMAP, POP3 or SMTP using <a href=\"\/mail\" title=\"Mail.dll email client\">Mail.dll .net email client<\/a>.<\/p>\n\n\n\n<p><strong>Make sure IMAP\/POP3\/SMTP is enabled<\/strong> for your organization and mailbox:<br><a href=\"\/blog\/office365-enable-imap-pop3-smtp\" title=\"Enable IMAP\/POP3\/SMTP on Office365\">Enable IMAP\/POP3\/SMTP in Office 365<\/a><\/p>\n\n\n\n<p><strong>Register your application<\/strong> in Azure Portal, here&#8217;s a detailed guide how to do that:<br><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/quickstart-register-app\">https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/quickstart-register-app<\/a> <\/p>\n\n\n\n<p>Remember to add authentication entries (localhost is needed for .net core):<\/p>\n\n\n\n<figure class=\"wp-block-image\"><img loading=\"lazy\" decoding=\"async\" width=\"777\" height=\"582\" src=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2020\/11\/image.png\" alt=\"\" class=\"wp-image-5764\" srcset=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2020\/11\/image.png 777w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2020\/11\/image-300x225.png 300w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2020\/11\/image-768x575.png 768w\" sizes=\"(max-width: 777px) 100vw, 777px\" \/><\/figure>\n\n\n\n<p> <\/p>\n\n\n\n<h3 class=\"wp-block-heading\">RedirectUri<\/h3>\n\n\n\n<p>.NET desktop: <strong>https:\/\/login.microsoftonline.com\/common\/oauth2\/nativeclient<\/strong><br>.NET core\/.NET 5,6,7+: <strong>http:\/\/localhost<\/strong><br>ASP.NET: your application custom url<\/p>\n\n\n\n<p><\/p>\n\n\n\n<p>Then you need to <strong>apply correct API permissions<\/strong> and grant the admin consent for your domain. <\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-25.png\"><img loading=\"lazy\" decoding=\"async\" width=\"897\" height=\"481\" src=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-25.png\" alt=\"\" class=\"wp-image-6057\" srcset=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-25.png 897w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-25-300x161.png 300w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-25-768x412.png 768w\" sizes=\"(max-width: 897px) 100vw, 897px\" \/><\/a><\/figure><\/div>\n\n\n<p>In the&nbsp;<strong>API permissions<\/strong> \/ <strong>Add a permission&nbsp;<\/strong>wizard, select&nbsp;<strong>Microsoft Graph<\/strong>&nbsp;and then&nbsp;<strong>Delegated permissions<\/strong>&nbsp;to find the following permission scopes listed:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li> offline_access<\/li>\n\n\n\n<li> email<\/li>\n\n\n\n<li> IMAP.AccessAsUser.All <\/li>\n\n\n\n<li> POP.AccessAsUser.All <\/li>\n\n\n\n<li> SMTP.Send <\/li>\n<\/ul>\n\n\n\n<p>Remember to <strong>Grant admin consent<\/strong>:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/10\/image-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"901\" height=\"477\" src=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/10\/image-2.png\" alt=\"\" class=\"wp-image-6341\" srcset=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/10\/image-2.png 901w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/10\/image-2-300x159.png 300w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/10\/image-2-768x407.png 768w\" sizes=\"(max-width: 901px) 100vw, 901px\" \/><\/a><\/figure>\n\n\n\n<p>Use&nbsp;Microsoft Authentication Library for .NET (MSAL.NET) nuget package to obtain an access token:<br><a href=\"https:\/\/www.nuget.org\/packages\/Microsoft.Identity.Client\/\">https:\/\/www.nuget.org\/packages\/Microsoft.Identity.Client\/<\/a> <\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstring clientId = &quot;Application (client) ID&quot;;\nstring tenantId = &quot;Directory (tenant) ID&quot;;\n\n\/\/ for @outlook.com\/@hotmail accounts instead of setting .WithTenantId use:\n\/\/ .WithAuthority(AadAuthorityAudience.PersonalMicrosoftAccount)\n\nvar app = PublicClientApplicationBuilder\n                .Create(clientId)\n                .WithTenantId(tenantId)\n                .WithDefaultRedirectUri()\n                .Build();\n\/\/ This allows saving access\/refresh tokens to some storage\nTokenCacheHelper.EnableSerialization(app.UserTokenCache);\n\nvar scopes = new string&#x5B;] \n{\n    &quot;offline_access&quot;,\n    &quot;email&quot;,\n    &quot;https:\/\/outlook.office.com\/IMAP.AccessAsUser.All&quot;,\n    &quot;https:\/\/outlook.office.com\/POP.AccessAsUser.All&quot;,\n    &quot;https:\/\/outlook.office.com\/SMTP.Send&quot;,\n};\n<\/pre><\/div>\n\n\n<p>In addition, you should request <a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/v2-permissions-and-consent#offline_access\">offline_access<\/a>&nbsp;scope. When a user approves the offline_access scope, your app can receive refresh tokens from the Microsoft identity platform token endpoint. Refresh tokens are long-lived. Your app can get new access tokens as older ones expire. <\/p>\n\n\n\n<p>Now acquire the access token and user email address:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstring userName;\nstring accessToken;\n\nvar account = (await app.GetAccountsAsync()).FirstOrDefault();\n\ntry\n{\n    AuthenticationResult refresh = await app\n        .AcquireTokenSilent(scopes, account)\n        .ExecuteAsync();\n\n    userName = refresh.Account.Username;\n    accessToken = refresh.AccessToken;\n}\ncatch (MsalUiRequiredException e)\n{\n    var result = await app.AcquireTokenInteractive(scopes)\n        .ExecuteAsync();\n\n    userName = result.Account.Username;\n    accessToken = result.AccessToken;\n}\n<\/pre><\/div>\n\n\n<p>On the first run user will see a Microsoft login screen, with option to log-in, using a known account and granting access to the app (if needed):<\/p>\n\n\n<div class=\"wp-block-image\">\n<figure class=\"aligncenter size-full\"><a href=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/08\/image-2.png\"><img loading=\"lazy\" decoding=\"async\" width=\"470\" height=\"479\" src=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/08\/image-2.png\" alt=\"\" class=\"wp-image-6273\" srcset=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/08\/image-2.png 470w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/08\/image-2-294x300.png 294w\" sizes=\"(max-width: 470px) 100vw, 470px\" \/><\/a><\/figure><\/div>\n\n\n<p>Finally you can connect using IMAP\/POP3\/SMTP, authenticate and download user&#8217;s emails:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nusing (Imap client = new Imap())\n{\n    client.ConnectSSL(&quot;outlook.office365.com&quot;);\n    client.LoginOAUTH2(userName, accessToken);\n \n    client.SelectInbox();\n\n    List&lt;long&gt; uids = imap.Search(Flag.Unseen);\n    foreach (long uid in uids)\n    {\n        IMail email = new MailBuilder()\n                .CreateFromEml(imap.GetMessageByUID(uid));\n        string subject = email.Subject;\n   }\n\n    client.Close();\n} \n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Any organization and personal accounts<\/h2>\n\n\n\n<p>To access accounts from any organization and personal accounts as well, you need to specify correct account types when you create the App in your AD:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/08\/image.png\"><img loading=\"lazy\" decoding=\"async\" width=\"697\" height=\"414\" src=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/08\/image.png\" alt=\"\" class=\"wp-image-6268\" srcset=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/08\/image.png 697w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/08\/image-300x178.png 300w\" sizes=\"(max-width: 697px) 100vw, 697px\" \/><\/a><\/figure>\n\n\n\n<p>Additionally you need to use:&nbsp;&nbsp;&nbsp;&nbsp;<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n    .WithAuthority(\n        AadAuthorityAudience.AzureAdAndPersonalMicrosoftAccount\n        )\n<\/pre><\/div>\n\n\n<p>instead of<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n    .WithTenantId(tenantId)\n<\/pre><\/div>\n\n\n<p>when creating the app:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\n var app = PublicClientApplicationBuilder\n    .Create(clientId)\n    .WithAuthority(\n        AadAuthorityAudience.AzureAdAndPersonalMicrosoftAccount\n        )\n    .WithDefaultRedirectUri()\n    .Build();\n<\/pre><\/div>\n\n\n<h2 class=\"wp-block-heading\">Token serialization<\/h2>\n\n\n\n<p>Below is a simple implementation that saves MSAL token cache to file:<\/p>\n\n\n<div class=\"wp-block-syntaxhighlighter-code \"><pre class=\"brush: csharp; title: ; notranslate\" title=\"\">\nstatic class TokenCacheHelper\n{\n    public static void EnableSerialization(ITokenCache tokenCache)\n    {\n        tokenCache.SetBeforeAccess(BeforeAccessNotification);\n        tokenCache.SetAfterAccess(AfterAccessNotification);\n    }\n\n    private static readonly string _fileName = &quot;msalcache.bin3&quot;;\n\n    private static readonly object _fileLock = new object();\n\n\n    private static void BeforeAccessNotification(TokenCacheNotificationArgs args)\n    {\n        lock (_fileLock)\n        {\n            byte&#x5B;] data = null;\n            if (File.Exists(_fileName))\n                data = File.ReadAllBytes(_fileName);\n            args.TokenCache.DeserializeMsalV3(data);\n        }\n    }\n\n    private static void AfterAccessNotification(TokenCacheNotificationArgs args)\n    {\n        if (args.HasStateChanged)\n        {\n            lock (_fileLock)\n            {\n                byte&#x5B;] data = args.TokenCache.SerializeMsalV3();\n                File.WriteAllBytes(_fileName, data);\n            }\n        }\n    }\n};\n<\/pre><\/div>\n\n\n<p>Please note that most likely you should store this cache in an encrypted form in some kind of a database.<br>Consider using MSAL token serialization implementations available here:<\/p>\n\n\n\n<p><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/msal-net-token-cache-serialization\">https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/msal-net-token-cache-serialization<\/a><\/p>\n\n\n\n<br \/>\n<a class=\"btn btn-primary btn-largest btn-action\" href=\"\/mail\/download\">Get Mail.dll<\/a>\n<br \/>\n","protected":false},"excerpt":{"rendered":"<p>In this series: &nbsp; OAuth 2.0 with Office365\/Exchange IMAP\/POP3\/SMTP OAuth 2.0 web flow with Office365\/Exchange IMAP\/POP3\/SMTP OAuth 2.0 password grant with Office365\/Exchange IMAP\/POP3\/SMTP OAuth 2.0 device flow with Office365\/Exchange IMAP\/POP3\/SMTP OAuth 2.0 client credential flow with Office365\/Exchange IMAP\/POP3\/SMTP This article shows how to implement OAuth 2.0 desktop flow to access Office365 via IMAP, POP3 or [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[4],"tags":[28,84,122,42,50],"class_list":["post-5649","post","type-post","status-publish","format-standard","hentry","category-mail-dll","tag-imap","tag-oauth-2-0","tag-office365","tag-pop3","tag-smtp"],"aioseo_notices":[],"_links":{"self":[{"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/posts\/5649"}],"collection":[{"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/comments?post=5649"}],"version-history":[{"count":60,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/posts\/5649\/revisions"}],"predecessor-version":[{"id":6549,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/posts\/5649\/revisions\/6549"}],"wp:attachment":[{"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/media?parent=5649"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/categories?post=5649"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/tags?post=5649"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}