Skip to main content

Using 2 Authentication factors (for MFA) in an unattended PowerShell Script

MFA (Multi Factor Authentication) is great at making the Authentication process more secure in Exchange Online but can be challenging in Automation scenarios. I originally wrote this code for something that I wanted to run unattended on a RasberryPi that was running PowerShell that i wanted to use MFA on and where i wanted to avoid going down the path of using the 90 day RefreshToken/device code method and I also didn't want to use App Authentication via Certificates or Client Secrets.

Interestingly while i was writing this post Microsoft just announced Certificate based Modern Auth in Exchange Online PowerShell  .  This article also links to the Secure App Model which is the way Microsoft are recommending you handle MFA in unattended delegate scenarios so this is an alternative to this if you can't go down that path .

One thing to note about any unattended method is that by there nature (eg because they aren't interactive) they can all potentially be unraveled by a bad actor if they make it passed the outer security of the machine where the script is running. Eg an unlocked machine or week logon security to that machine that would allow an attacker to access the stored RefreshToken/Certificate/SharedKey (no matter where they are stored or how they are encrypted). However the more factors you can put into your unattended process eg encrypting the stored cert/key etc the slower its going to be for anybody trying to unravel it. The gold standard would be an Secure Azure VM where the secrets themselves are in a KeyVault and use a managed identity but in the real world this is not always possible. In security terms you should always consider what the implications are when the security around your unattended process fails (and not if it fails). 


The two factors in my script that I'm going to be using is firstly a Username and Password which is really the only Primary Authentication method that currently lends itself to be used unattended (eg FIDO and OATH requires some level of interactivity). The additional second factor I'm using is TOTP (Time based One Time passwords) which are relatively easy to generate as they are based on . Other people have already created a few Powershell functions for generating TOTP's so the one I decided to use was which just requires that you pass in the SharedSecret that you get when you add a (3rd Party Authenticator)to your account in Office365 as an additional authentication method . To do this you should select to add an Authenticator application as an additional authentication method and select the "Configure app without notifications" from the screen that shows the QRCode. You then should be presented with a secret (shared)key (if it has spaces in it you will need to remove these) which you then need to feed into the Get-TimeBasedOneTimePassword cmdlet which will provide the verification code to successfully add the authenticator eg

Get-TimeBasedOneTimePassword -SharedSecret txxxxxxxxxxxx

You then need to store this sharedsecret as securely as you can as you will use this each time you want to logon to generate the TOTP factor for the authentication. A few ways of storing this is firstly use an Azure key vault, if you have a Windows machine your running it on then consider the credential store which can be used easily via another Powershell module and there's a simple demo of using it . (I would suggest you put a second layer of encryption on top of this so whatever goes into the credential store is also encrypted)

MFA Code

So far I've been using a whole bunch of other peoples code but now comes to the bit i wrote which does the MFA authentication using OpenId connect and then gets an AccessToken once an authorization_code is acquired. This code is relatively straight forward it starts the normal browser based authentication flow maintaining the session using Invoke-WebRequest's -WebSession parameter and parses out the context, flow and canary tokens from the response which are needed in various parts of the process. For the additional authentication factor the TOTP is used.


While my code was written for EWS primarily running on a Rasberry PI a more relevant sample is a Exchange Online PowerShell connection eg 

So this simple example uses the CredentialManger PS module to grab a ShareSecret from the credential store and generate a TOTP and then you have a normal set of PSCredentials and the "BasicAuthToOAuthConversion=true" query-string does the rest.

A few things to remember is that Access Tokens are only good for 1 hour so if you expect your script to run for longer then this you will need to put in a method of refreshing the Token (or just generate a new access token). 

I've put the code for the AzureMFAOTPv2 module on GitHub here


Popular posts from this blog

Downloading a shared file from Onedrive for business using Powershell

I thought I'd quickly share this script I came up with to download a file that was shared using One Drive for Business (which is SharePoint under the covers) with Powershell. The following script takes a OneDrive for business URL which would look like This script is pretty simple it uses the SharePoint CSOM (Client side object Model) which it loads in the first line. It uses the URI object to separate the host and relative URL which the CSOM requires and also the SharePointOnlineCredentials object to handle the Office365 SharePoint online authentication. The following script is a function that take the OneDrive URL, Credentials for Office365 and path you want to download the file to and downloads the file. eg to run the script you would use something like ./spdownload.ps1 '

A walk-though using the Graph API Mailbox reports in Powershell

Quite recently the Reporting side of the Graph API has moved in GA from beta, there are quite a number of reports that can be run across various Office365 surfaces but in this post I'm going to focus on the Mailbox related ones. Accessing Office365 Reports using Powershell is nothing new and has been available in the previous reporting endpoint however from the end of January many of these cmdlets are now being depreciated in favour of the Graph API . Prerequisites  In comparison to using the Remote PowerShell cmdlets where only the correct Office365 Admin permissions where needed, to use the new Graph API reports endpoint you need to use OAuth for authentication so this requires an Application Registration  that is then given the correct oAuth Grants to use the Reports EndPoin

How to test SMTP using Opportunistic TLS with Powershell and grab the public certificate a SMTP server is using

Most email services these day employ Opportunistic TLS when trying to send Messages which means that wherever possible the Messages will be encrypted rather then the plain text legacy of SMTP.  This method was defined in RFC 3207 "SMTP Service Extension for Secure SMTP over Transport Layer Security" and  there's a quite a good explanation of Opportunistic TLS on Wikipedia .  This is used for both Server to Server (eg MTA to MTA) and Client to server (Eg a Message client like Outlook which acts as a MSA) the later being generally Authenticated. Basically it allows you to have a normal plain text SMTP conversation that is then upgraded to TLS using the STARTTLS verb. Not all servers will support this verb so if its not supported then a message is just sent as Plain text. TLS relies on PKI certificates and the administrative issue s that come around certificate management like expired certificates which is why I wrote th
All sample scripts and source code is provided by for illustrative purposes only. All examples are untested in different environments and therefore, I cannot guarantee or imply reliability, serviceability, or function of these programs.

All code contained herein is provided to you "AS IS" without any warranties of any kind. The implied warranties of non-infringement, merchantability and fitness for a particular purpose are expressly disclaimed.