{"id":5988,"date":"2022-03-28T15:18:36","date_gmt":"2022-03-28T13:18:36","guid":{"rendered":"https:\/\/www.limilabs.com\/blog\/?p=5988"},"modified":"2024-10-07T12:18:02","modified_gmt":"2024-10-07T10:18:02","slug":"oauth2-device-flow-office365-exchange-imap-pop3-smtp","status":"publish","type":"post","link":"https:\/\/www.limilabs.com\/blog\/oauth2-device-flow-office365-exchange-imap-pop3-smtp","title":{"rendered":"OAuth 2.0 device flow with Office365\/Exchange IMAP\/POP3\/SMTP"},"content":{"rendered":"\n<div class=\"well\">\nIn this series:\n<p>&nbsp;<\/p>\n<ul>\n<li><a href=\"https:\/\/www.limilabs.com\/blog\/oauth2-office365-exchange-imap-pop3-smtp\">OAuth 2.0 with Office365\/Exchange IMAP\/POP3\/SMTP<\/a><\/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>OAuth 2.0 device flow with Office365\/Exchange IMAP\/POP3\/SMTP<\/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 device 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>Device flow allows operator\/administrator to authenticate your application on a different machine than your application is installed.<\/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>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-27.png\"><img loading=\"lazy\" decoding=\"async\" width=\"897\" height=\"481\" src=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-27.png\" alt=\"\" class=\"wp-image-6059\" srcset=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-27.png 897w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-27-300x161.png 300w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-27-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-4.png\"><img loading=\"lazy\" decoding=\"async\" width=\"901\" height=\"477\" src=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/10\/image-4.png\" alt=\"\" class=\"wp-image-6343\" srcset=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/10\/image-4.png 901w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/10\/image-4-300x159.png 300w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/10\/image-4-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\nIPublicClientApplication app = PublicClientApplicationBuilder\n    .Create(clientId)\n    .WithTenantId(tenantId)\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>Now acquire an access token and a user name: <\/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();\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 acquire = await app.AcquireTokenWithDeviceCode(\n        scopes, \n        callback=&gt;\n    {\n        \/\/ Write url and code to logs so the operator can react:\n        Console.WriteLine(callback.VerificationUrl);\n        Console.WriteLine(callback.UserCode);\n\n        \/\/ This happens on the first run, manually,\n        \/\/  on the operator machine.\n        \/\/ The code below code is only to illustrate \n        \/\/ the operator opening browser on his machine,\n        \/\/ opening the url and using the code \n        \/\/ (extracted from the application logs)\n        \/\/ to authenticate the app.\n        System.Diagnostics.Process.Start(\n            new ProcessStartInfo(callback.VerificationUrl) \n                        { UseShellExecute = true }\n            );\n\n        return Task.CompletedTask;\n    }).ExecuteAsync();\n\n    userName = acquire.Account.Username;\n    accessToken = acquire.AccessToken;\n}\n\n<\/pre><\/div>\n\n\n<p><strong>AcquireTokenWithDeviceCode <\/strong>call waits until operator\/administrator gives consent by going to <strong>VerificationUrl<\/strong>, entering <strong>UserCode <\/strong>and authenticating &#8211; this usually happens on a different machine than the application is installed.<\/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-18.png\"><img loading=\"lazy\" decoding=\"async\" width=\"601\" height=\"509\" src=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-18.png\" alt=\"\" class=\"wp-image-6005\" srcset=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-18.png 601w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-18-300x254.png 300w\" sizes=\"(max-width: 601px) 100vw, 601px\" \/><\/a><\/figure><\/div>\n\n\n<p><\/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-20.png\"><img loading=\"lazy\" decoding=\"async\" width=\"605\" height=\"365\" src=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-20.png\" alt=\"\" class=\"wp-image-6007\" srcset=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-20.png 605w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-20-300x181.png 300w\" sizes=\"(max-width: 605px) 100vw, 605px\" \/><\/a><\/figure><\/div>\n\n\n<p>Finally your app will exit <strong>AcquireTokenWithDeviceCode <\/strong>method and connect using IMAP\/POP3\/SMTP, authenticate and download 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    \/\/ ...\n\n    client.Close();\n} \n<\/pre><\/div>\n\n\n<p>You can find more details on this flow here:<\/p>\n\n\n\n<p><a href=\"https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/v2-oauth2-device-code\">https:\/\/docs.microsoft.com\/en-us\/azure\/active-directory\/develop\/v2-oauth2-device-code<\/a><\/p>\n\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. Please note that most likely you should store this cache in an encrypted form:<\/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>More details on MSAL token serialization are 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<h2 class=\"wp-block-heading\">Extending Sign-in frequency with policies<\/h2>\n\n\n\n<p>You can extend how often operator needs to re-authenticate the application up to 1 year:<\/p>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-16.png\"><img loading=\"lazy\" decoding=\"async\" width=\"488\" height=\"394\" src=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-16.png\" alt=\"\" class=\"wp-image-6003\" srcset=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-16.png 488w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-16-300x242.png 300w\" sizes=\"(max-width: 488px) 100vw, 488px\" \/><\/a><\/figure>\n\n\n\n<figure class=\"wp-block-image size-full\"><a href=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-17.png\"><img loading=\"lazy\" decoding=\"async\" width=\"941\" height=\"697\" src=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-17.png\" alt=\"\" class=\"wp-image-6004\" srcset=\"https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-17.png 941w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-17-300x222.png 300w, https:\/\/www.limilabs.com\/blog\/wp-content\/uploads\/2022\/03\/image-17-768x569.png 768w\" sizes=\"(max-width: 941px) 100vw, 941px\" \/><\/a><\/figure>\n\n\n\n<p>Side note: Have in mind that similarly a <strong>client credential flow <\/strong>requires a client secret which is valid for 2 years maximum.<\/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 device 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":[1],"tags":[28,84,122,42,50],"class_list":["post-5988","post","type-post","status-publish","format-standard","hentry","category-news","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\/5988"}],"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=5988"}],"version-history":[{"count":32,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/posts\/5988\/revisions"}],"predecessor-version":[{"id":6656,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/posts\/5988\/revisions\/6656"}],"wp:attachment":[{"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/media?parent=5988"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/categories?post=5988"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.limilabs.com\/blog\/wp-json\/wp\/v2\/tags?post=5988"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}