Hacking your way around Modern authentication and the PowerShell modules for Office 365

Most of the Office 365 PowerShell modules now support Modern authentication and that’s a very good thing. However, the implementation across the different modules leaves a lot to be desired because of the different approach taken by each team. For example, some of the modules do not support the Credentials parameter, other support it but automatically fallback to using legacy authentication when this parameter is invoked and so on. The table below summarizes the current landscape:

MFA status Pass credentials Pass token Bypass MFA on trusted location
Azure AD Supported Supported Supported Supported
Exchange Online (legacy) Not supported N/A N/A Not supported
Exchange Online (MFA module) Supported Not supported Not supported* Supported
Security and Compliance Center Not supported N/A N/A Not supported
SharePoint Online Supported Supported*** Not supported Not supported
SharePoint Online PnP Supported Supported*** Not supported Supported
Skype for Business Online Supported Supported*** Not supported* Supported
AIP/AADRM Supported Supported Supported Supported
Azure Supported Supported Supported Supported

The option to pass credentials is an important one, as you can combine it with different methods to bypass the need to perform second-factor authentication. For example, you can modify your claims rules to ensure that when the request is coming from a particular user, IP or application, no additional authentication will be required. Similarly, if you have configured second-factor authentication via Azure MFA, you can use the trusted locations functionality. This enables you to use the account to automate scripts or execute scheduled tasks, while at the same time still making sure it’s protected by additional authentication factors for generic requests.

(The asterisks in the Pass credentials column designate the modules which use legacy authentication when the Credentials parameter is invoked)

An alternative to this approach is to completely bypass the PowerShell modules and get an access token programmatically, then pass it directly, which is what we will discuss in the current article. Some of the modules do have support for passing a token as noted in the above table. The asterisks in the Pass token column indicate modules that do not have a parameter to pass the access token to, but for which certain workarounds exist.

Anyway, in order to get the token programmatically, one can use the ADAL binaries that come with the install of any of the above modules. For example, if you have the version of the Azure AD PowerShell module installed, you can load the necessary DLL via:

Add-Type -Path 'C:\Program Files\WindowsPowerShell\Modules\AzureAD\\Microsoft.IdentityModel.Clients.ActiveDirectory.dll'

The same DLL is shipped with each of the ADAL-enabled modules, however the version of the DLL might be different! In turn, this results in different methods exposed and even different authentication flows! This is one of the examples of the different ways modern authentication support has been implemented by different teams at Microsoft and one can only hope in the future things will change for the better.

Once the DLL is loaded, one can use the AcquireTokenAsync method to acquire a new access token. Some information is required beforehand though, such as the authority from which to request the token, the clientID of the application (Exchange Online for example), the resource you want access to. Unfortunately, there is no easy way to get the clientID, as Microsoft does not publish a list of the “server-side” applications. However, we can simply “hack” into what the ExO module does for example, and get all the needed information from a Fiddler trace. Or better yet, simply get it from the token cache:

$cache = [Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache]::DefaultShared
$cache.ReadItems() | select DisplayableId, Authority, ClientId, Resource

The screenshot above is taken after connecting to the Azure AD, ExO and SfBO PowerShell modules with Modern authentication enabled. For each of these, an access token was obtained and the token cache gives us information about the authority, clientID and Resource for which the token is valid. In effect, now we have all the needed information to get a token programmatically for each of these. Here’s how to get the token for ExO:

$authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList https://login.microsoftonline.com/michev.onmicrosoft.com/
$client_id = "a0c73c16-a7e3-4564-9a95-2bdf47383716"
$Credential = Get-Credential huku@michev.onmicrosoft.com
$AADcredential = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserPasswordCredential" -ArgumentList $Credential.UserName,$Credential.Password
$authResult = [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContextIntegratedAuthExtensions]::AcquireTokenAsync($authContext,"https://outlook.office365.com",$client_Id,$AADcredential)

If everything went OK, the access token will be contained in the $authResult.Result.AccessToken output. Or, you can simply look at the token cache as we did above. And once you have the access token, you can then connect to ExO Remote PowerShell by using:

$Authorization = "Bearer {0}" -f $authResult.Result.AccessToken
$Password = ConvertTo-SecureString -AsPlainText $Authorization -Force
$Ctoken = New-Object System.Management.Automation.PSCredential -ArgumentList "huku@michev.onmicrosoft.com", $Password
$Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/PowerShell-LiveId?BasicAuthToOAuthConversion=true -Credential $Ctoken -Authentication Basic -AllowRedirection
Import-PSSession $Session

There’s one last piece of information that we needed in order to perform the above action, namely the ConnectionURI. You can see that it’s very similar to the “regular” one we’ve been using for years, however there is one additional parameter added at the end. Where do we get this information you ask? Simply look at the parameters of any ExO Remote PowerShell session you have created via the new, MFA-enabled module:

The procedure outlined above can be used to “reverse engineer” connecting to other Office 365 services via access token. Getting the access token itself can be performed by various means, using the different ADAL methods. For the purposes of automation, a solution that does not require any user interaction is preferred, so my recommended method is to configure the bypass, however you can also invoke the generic ADAL prompt and perform the login process interactively if needed.

Now, one important thing that needs to be made clear – this only covers the access token! Depending on the ADAL binaries used, a refresh token might not even be returned, so the session you establish using this method will be a short-lived one 🙂

This entry was posted in Exchange Online, Office 365, PowerShell. Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *