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 2.0.0.98 version of the Azure AD PowerShell module installed, you can load the necessary DLL via:

Add-Type -Path 'C:\Program Files\WindowsPowerShell\Modules\AzureAD\2.0.0.98\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

062417 1414 Hackingyour1

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/tenantname.onmicrosoft.com/
$client_id = "a0c73c16-a7e3-4564-9a95-2bdf47383716"
$Credential = Get-Credential user@tenantname.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 "user@tenantname.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:

062417 1414 Hackingyour2

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 🙂

71 thoughts on “Hacking your way around Modern authentication and the PowerShell modules for Office 365

  1. Pingback: 当基本身份验证不再支持时,如何运行Exchange Online远程powershell服务器到服务器? – 实战宝典
  2. Yannik says:

    Adding to my previous comment:

    The access tokens can be acquired using:

    $context = [Microsoft.Exchange.Management.ExoPowershellSnapin.ConnectionContextFactory]::GetAllConnectionContexts()
    $context.TokenProvider[0].GetAccessToken()

    Unfortunately, I couldn’t find a method to connect to exchange online using an existing access token with exo v2 (like it was possible previously with import-pssession)

    Reply
  3. Yannik says:

    Hi Vasil,

    do you have any idea where the access token might be stored for the exo v2 modules?

    Unforunately, the cache returned by [Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache]::DefaultShared is always empty. (Tested with 2.0.5)

    Reply
  4. Pingback: 当基本身份验证不再支持时,如何运行Exchange Online远程powershell服务器到服务器? - 实战宝典
  5. Venkata Rallapalli says:

    Hi Vasil,

    As of May 17th, 2021 – Microsoft retired the SFBO connector. With this, the ability to hack around the modern authentication with the Basic OAUTH is also going to be turned off or is already turned off.

    Do you have any suggestions for the alternative form of authentication for a smoother integration? We have been trying the Connect-MicrosoftTeams with AadAccessToken and that is not working. Our backend integration is broken because of this.

    Thanks,
    Venkata

    Reply
  6. Peter Williams says:

    I haven’t read the article thoroughly, but from what I have read thus far, I’m finding it really useful. Thank you very much!

    I haven’t been able to overcome an issue of authentication when using the Project Online CSOM library when the account has MFA enabled. It is a real issue. I was able to figure it out when using the SharePoint Online CSOM library though.

    For a new client I have, they turned off MFA for the account we use, but I still can’t authenticate; I believe due to them disabling legacy authentication but that is what I’m trying to confirm.

    Reply
  7. Ankita says:

    Hi Vasil,

    I am trying to enable the modern authentication using Powershell Script. Currently we have a PS which does the following:
    -Uses basic authentication
    -imports a .reg file
    -connects to the Microsoft-online and loads the data in the on premises Sql DB.
    For all the above tasks, we are using a service account, which has the admin rights. Below is the snippet of the code:
    $credentials = GetCredsPS
    $session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/powershell-liveid/ -Credential $credentials -Authentication Basic -AllowRedirection
    Import-PSSession $session
    Now we want to use the same script and migrate to modern authentication. I have gone through many articales regarding this, but couldnt get the below points:
    -Do i need to add a registry key explicitly for modern authentication?
    -I have ran the command: Import-Module ExchangeOnlineManagement
    -Do i need to connect to Azure AD and MSol? Since the current one with basic authentication doesnt have the connection to Azure AD and MSol.
    -when I run the command: Connect-ExchangeOnline -Credential $creds, it succesfully runs with no exception and with the message :Removed the PSSession ExchangeOnlineInternalSession_10 connected to outlook.office365.com
    -When i ran this command:
    (Get-PSSession).Runspace.ConnectionInfo.ConnectionUri
    I see the ?BasicAuthToOAuthConversion=true
    what else do i need to perform apart from all the above tasks mentioned to enable the modern authentication. Please advise

    Reply
  8. patrick says:

    A little late to the party but I will see.

    I have a registered app in azure and I can get a token no issue, based on your code I would need to get a separate token using the user@myorg.onmicrosoft.com and use that token to create my $Ctoken right? the only problem that i’m facing is that [Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContextIntegratedAuthExtensions] is not found, even though the [Microsoft.IdentityModel.Clients. ActiveDirectory.dll] is loaded. Any advice? currently running on dll version 3.14.40626.546

    Reply
    1. Vasil Michev says:

      You cannot use a custom app, only the built-in one as included in the examples above. As for the ADAL methods, the code was written for an older version. Simply use whichever method you want, or get the token directly by web call.

      Reply
      1. Bronson Lee says:

        I was able to use this method on a custom azure web app.
        I used this method to receive an access token from login.microsoftonline.com , and then validated the access token against my custom azure web app and then received an Authentication Token back.

        Reply
  9. Shaun Blackmore says:

    Hi – Do you know if this method will still work after Basic Auth is retired in October 2020? Thanks.

    Reply
    1. Vasil Michev says:

      That’s for Microsoft to answer 🙂 They’re supposedly working on “true” Modern authentication solution for PS, but we’ll see.

      Reply
  10. Volodymyr says:

    Hi Vasil,

    Is it possible to use Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential class to obtain an authentication token and then use this token with the described method?

    Thanks

    Reply
    1. Vasil Michev says:

      You can get the token, but wont be able to use it to connect via PowerShell, as it always runs in the context of a given user. Microsoft is supposedly working on a solution for the app permissions model.

      Reply
      1. Volodymyr says:

        Thanks for the answer

        So currently the only application that can be used to connect to Exchange via Power Shell is the application with ID a0c73c16-a7e3-4564-9a95-2bdf47383716. Correct?

        Reply
        1. Vasil Michev says:

          There are probably others, this is simply the one used by the PS module.

  11. Jeremy Engel says:

    Hi Vasil,

    Your code works great! Now, I’m trying to extrapolate your code to use with an MSAL token to establish the remote session with EXO, but it’s not working. Does EXO even support MSAL? If so, what am I missing? ANy help you can give would be greatly appreciated.

    $msalToken = Get-MSALToken -ClientId a0c73c16-a7e3-4564-9a95-2bdf47383716 -TenantId mytenant.onmicrosoft.com -RedirectUri urn:ietf:wg:oauth:2.0:oob
    $msalCred = New-Object System.Management.Automation.PsCredential($msalToken.Account.Username,(ConvertTo-SecureString -String “Bearer $($msalToken.AccessToken)” -AsPlainText -Force))

    $exoSession = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://outlook.office365.com/PowerShell-LiveId?BasicAuthToOAuthConversion=true -Credential $msalCred -Authentication Basic -AllowRedirection

    Jeremy

    Reply
    1. Vasil Michev says:

      No idea, haven’t bothered to check it yet 🙂

      Reply
  12. Mark Wilson says:

    I am trying to get this working for Connect-MSOLService…the ADAccessToken and GraphAccessToken parameters are not working for me. Is there an equivalent Credential object format we can use for that instead?
    or else do you have a working example for Connect-MSOLService?

    Reply
  13. Amie says:

    Hi Vasil,

    Just i have one question, is there anyway i can connect to office services via WIn Powershell or Exchange Online Remote Powershell for URI “https://webmail.*domain*.net/powershell-liveid/”.
    Believe Basic Auth at WInRM should be enabled/mandatory for both, but from organization perspective we have blocked the same.

    Reply
    1. Vasil Michev says:

      You need to have Basic Auth enabled on WinRM, no other way around it.

      Reply
        1. Vasil Michev says:

          You are using a remote environment there, it doesn’t depend what your own WinRM settings are.

  14. Venkata Rallapalli says:

    Hi Vasil,

    Thank you very very much for your timely help. We were able to fix the issue. I guess the following fix is because of the latest version of SFB Connector or something. Here is the script that worked for us:

    The client ID is as follows:

    $client_id = “7716031e-6f8b-45a4-b82b-922b1af0fbb4”

    After we get the auth token from ADAL using any programming language, we can straight do the following:

    $Authorization = $authResult.Result.AccessToken // ***not required to added “Bearer ” upfront***
    $user = “oauth”
    $pass = ConvertTo-SecureString -AsPlainText $Authorization -Force
    $Cred = New-Object System.Management.Automation.PSCredential $user,$pass
    $Session = New-PSSession -ConnectionUri “https://adminXX.online.lync.com/OcsPowershellOAuth?AdminDomain=mydomain.com&BasicAuthToOAuthConversion=true” -Credential $Cred -AllowRedirection -ConfigurationName Microsoft.PowerShell -Authentication basic

    Thanks,
    –Venkata

    Reply
    1. Vasil Michev says:

      Glad you got it working, I haven’t updated the SfBO PowerShell module in a while so I guess this is why you are seeing a different client ID.

      Reply
  15. Naqash says:

    I connected ExO with Exchange Online Powershell Module but i did not get any token saved in cache and the count is 0 (zero). There is no error in the script but still it says count = 0. Please help.

    Reply
    1. Vasil Michev says:

      The availability of the token cache depends on the version of the ADAL binaries used, the way the token cache is exposed/shared and the way the token was obtained. If you are using the MFA-enabled ExO PowerShell module, you should be able to see it right after connecting via:

      $cache = [Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache]::DefaultShared
      $cache.ReadItems()

      Reply
  16. Venkata Rallapalli says:

    Hi Vasil,

    Thanks for your quick response. From the article above when I used the cache token, I got a different Client ID. So I was using it along with the resource URI which is also different to “admin0e”. I thought that the values there are specific to your environment.

    However, I tried with what you gave exactly with all the possible combinations by retaining the client id given by you:

    1. As it is given by you – gave the same 403 message
    2. Used my resource ‘adminXXa’ with my domain – gave the same 403 message
    3. Used the resource given by you with my domain – gave the same 403 message.

    If possible, can you send me an email to ‘kasaga311@gmail.com’? Want to discuss if you are able to do some consulting work for us for fixing this issue. Please let me know if you are available for the same.

    Thanks,
    Regards,
    –Venkata

    Reply
    1. Vasil Michev says:

      Get the access token and put it in some parser, such as JTW.ms. Then check if it has the relevant client ID and permissions/scopes.

      Reply
      1. Venkata Rallapalli says:

        Hi Vasil,

        The token has all the information from the Authentication. It includes the scope as fulluseraccess. The response from the JWT parser is as follows:

        {
        “aud”: “https://admin3a.online.lync.com/OcsPowershellOAuth”,
        “iss”: “https://sts.windows.net/ef0d1654-835d-4970-80e9-fd59fb2b4981/”,
        “iat”: 1566899724,
        “nbf”: 1566899724,
        “exp”: 1566903624,
        “acr”: “1”,
        “aio”: “42FgYDj44+YD7QsOk3qY67MnbU81a3k2KTS13WNd/VLHslCvqbYA”,
        “amr”: [
        “pwd”
        ],
        “appid”: “7716031e-6f8b-45a4-b82b-922b1af0fbb4”,
        “appidacr”: “0”,
        “family_name”: “MySurname”,
        “given_name”: “MyFullName”,
        “ipaddr”: “52.190.31.XXX”,
        “name”: “My Full Name”,
        “oid”: “MyUserGUID”,
        “puid”: “100320003190C4C4”,
        “scp”: “Fulluseraccess”,
        “sub”: “oeFPTsHkEJvLa1S5KCtorSX-mxmtzSSTJgZf6oQh2tA”,
        “tid”: “ef0d1654-835d-4970-80e9-fd59fb2b4981”,
        “unique_name”: “myloginID@myDomain.com”,
        “upn”: “myloginID@myDomain.com”,
        “uti”: “hdC3eHmd3ESFtSuFKAATAA”,
        “ver”: “1.0”,
        “wids”: [
        “62e90394-69f5-4237-9190-012177145e10”
        ]
        }

        The New-PSsession gives the following error:

        New-PSSession : [admin3a.online.lync.com] Connecting to remote server admin3a.online.lync.com failed with the
        following error message : The WinRM client cannot process the request. The authentication mechanism requested by the
        client is not supported by the server or unencrypted traffic is disabled in the service configuration. Verify the
        unencrypted traffic setting in the service configuration or specify one of the authentication mechanisms supported by
        the server. To use Kerberos, specify the computer name as the remote destination. Also verify that the client
        computer and the destination computer are joined to a domain. To use Basic, specify the computer name as the remote
        destination, specify Basic authentication and provide user name and password. Possible authentication mechanisms
        reported by server: For more information, see the about_Remote_Troubleshooting Help topic.

        Please let me know where I’m going wrong. The appID in the response above is what I’m getting when I login and check the token cache along with the discovered admin url which is also in the response above.

        Please let me know if you are able to do a quick call or a skype chat to discuss this issue further.

        Thanks & Regards,
        –Venkata

        Reply
  17. Venkata Rallapalli says:

    Hi Vasil,

    We are trying to automate the SFB Online script execution by obtaining an OAuth token using ADAL in a Web application. We were able to obtain the Token using the ADAL API in .NET Web app.

    Created the PS script with the following:

    $Authorization = “Bearer {token}”
    $Password = ConvertTo-SecureString -AsPlainText $Authorization -Force
    $Ctoken = New-Object System.Management.Automation.PSCredential(“ourUser@ourdomain.com”, $Password)

    After that, we tried to do both the following things:

    1. $sfbSession = New-CsOnlineSession -Credential $Ctoken
    2. $sfbSession = New-PSSession -ConnectionUri https://adminXXa.online.lync.com/OcsPowershellOAuth?BasicAuthToOAuthConversion=true -Credential $Ctoken -Authentication Basic -AllowRedirection

    Both ways, we are not able to make it work. In the first case, we are getting ‘Invalid Username or Password’.

    In the second case, we are getting “The WinRM client cannot process the request. The authentication mechanism requested by the client is not supported by the server or unencrypted traffic is disabled in the service configuration.”

    We are stuck at the moment. Request you to please help us by giving your inputs on where we are going wrong.

    Thanks,
    Venkata

    Reply
    1. Vasil Michev says:

      When you set you credentials variable for SfB, you need to use “oAuth” as the username. You can easily verify this if you look at the connection settings of the PowerShell session after you connect to SfBO via modern auth.

      [17:55:12][Login script]# $ses.Runspace.ConnectionInfo.Credential.GetNetworkCredential()

      UserName Domain
      ——– ——
      oauth

      Reply
      1. Venkata Rallapalli says:

        Dear Vasil,

        Thanks a lot for your quick response. I changed the Username to the suggestion made by you as follows:

        Import-Module SkypeOnlineConnector
        $Authorization = “Bearer {token}”
        $Password = ConvertTo-SecureString -AsPlainText $Authorization -Force
        $Ctoken = New-Object System.Management.Automation.PSCredential(“oAuth”, $Password)

        Case 1:
        $sfbSession = New-CsOnlineSession -Credential $Ctoken

        Response is –

        You must specify a user principal name in the format of User@Domain.Com.
        At C:\Program Files\Common Files\Skype for Business
        Online\Modules\SkypeOnlineConnector\SkypeOnlineConnectorStartup.psm1:87 char:13
        + throw ($resources.WrongUserName -f $($UserName))
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : OperationStopped: (You must specif…ser@Domain.Com.:String) [], RuntimeException
        + FullyQualifiedErrorId : You must specify a user principal name in the format of User@Domain.Com.

        Case 2:
        $Session = New-PSSession -ConnectionUri “https://adminXXa.online.lync.com/OcsPowershellOAuth?AdminDomain=ourdomain.com&BasicAuthToOAuthConversion=true&ConnectorVersion=7.0.1994.0” -Credential $Ctoken -Authentication Basic -AllowRedirection

        We are getting the following response –

        New-PSSession : [adminXXa.online.lync.com] Connecting to remote server adminXXa.online.lync.com failed with the
        following error message : The WinRM client cannot process the request. The authentication mechanism requested by the
        client is not supported by the server or unencrypted traffic is disabled in the service configuration. Verify the
        unencrypted traffic setting in the service configuration or specify one of the authentication mechanisms supported by
        the server. To use Kerberos, specify the computer name as the remote destination. Also verify that the client
        computer and the destination computer are joined to a domain. To use Basic, specify the computer name as the remote
        destination, specify Basic authentication and provide user name and password. Possible authentication mechanisms
        reported by server: For more information, see the about_Remote_Troubleshooting Help topic.
        At line:1 char:12
        + $Session = New-PSSession -ConnectionUri “https://adminXXa.online.lync …
        + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
        + CategoryInfo : OpenError: (System.Manageme….RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotin
        gTransportException
        + FullyQualifiedErrorId : AccessDenied,PSSessionOpenFailed

        Any insight into where I’m going wrong would be of immense help.

        We’ve enabled ‘Basic’ in WinRM client and Service settings on the machine. We’ve also enabled ‘Unencrypted Traffic’ and configured ‘Trusted Hosts’ under

        Edit Group Policy\Computer Management\Admin Templates\Windows Remote Management\…

        I appreciate your time on this.

        Thanks,
        Venkata

        Reply
        1. Vasil Michev says:

          Check your access token. Here’s a working example:

          $authContext2 = New-Object “Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext” -ArgumentList “https://login.windows.net/common/oauth2/authorize”
          $client_id = “d924a533-3729-4708-b3e8-1d2445af35e3”
          $temp = $authContext2.AcquireTokenSilentAsync(“https://admin0e.online.lync.com/OcsPowershellOAuth”, $client_id)

          $Username = “oauth”
          $Password = ConvertTo-SecureString -AsPlainText $temp -Force
          $Cred = New-Object System.Management.Automation.PSCredential -ArgumentList “oauth”, $Password

          New-PSSession -ConnectionUri “https://admin0e.online.lync.com/OcsPowershellOAuth?AdminDomain=michev.onmicrosoft.com” -Credential $Cred -AllowRedirection -ConfigurationName Microsoft.PowerShell -Authentication basic

        2. Venkata Rallapalli says:

          Hi Vasil,

          Thanks a tonne again for your quick response. I tried the snippet that you pasted with my client ID and our url. The response from our server is as follows:

          New-PSSession : [adminXXa.online.lync.com] Connecting to remote server admin3a.online.lync.com
          failed with the following error message : The WinRM client received an HTTP status code of 403
          from the remote WS-Management service. For more information, see the about_Remote_Troubleshooting
          Help topic.
          At line:1 char:1
          + New-PSSession -ConnectionUri “https://adminXXa.online.lync.com/OcsPowe …
          + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
          + CategoryInfo : OpenError: (System.Manageme….RemoteRunspace:RemoteRunspace) [New-P
          SSession], PSRemotingTransportException
          + FullyQualifiedErrorId : -2144108273,PSSessionOpenFailed

          I tried various fixes suggested through google in vain. Please let me know where I’m going wrong with this.

          Thanks,
          Regards,
          –Venkata

        3. Vasil Michev says:

          What do you mean *your* client ID? That’s the client ID of the SfB PowerShell application, it’s the same for all tenants.

  18. Tom says:

    Hi Vasil,

    First, thank you for spending the time to put this together. This method saved me big time when automating some long-running exchange tasks related to a migration. Now, I’m working on a migration to Skype Online and am running into a problem where after the token expires, it prompts for the ‘OAuth’ credentials. The work-around so far as been to close and re-open powershell. This is a problem because the limit of concurrent connections is quickly exceeded. I was hoping to use this method however, when setting up the connection, I receive the following error:

    New-PSSession : [admin0a.online.lync.com] Connecting to remote server admin0a.online.lync.com failed with the following error message : The WinRM client cannot process the request. The authentication mechanism requested by the client is not supported by the server or unencrypted traffic is disabled in the service configuration.

    I’ve tried it a few different configurations and have not had any luck. Any tips? This would be super helpful.

    Reply
    1. Vasil Michev says:

      For SfB, use “OAuth” as the username, and the token as the password.

      Reply
  19. Yasitha says:

    Hi Vasil,

    I was reading a lot of articles written by you for last few weeks as I was looking for a solution to use modern authentication for exchange powershell login.
    As per my research I understood that now exchange online powershell connection can be established using modern authentication, however, the access token should have the RemotePowerShell.AccessAsUser.All scope. Microsoft doesn’t expose this permissions to assign to an app via azure portal, so basically I cannot get permission to log into the powershell from users via our azure application, unless I use the customer app which is used internally in Connect-EXOPSSession.

    Do you have any idea or solution that works for background jobs which has to frequently login into users exchange online powershell with MFA enabled global admin?

    Reply
    1. Vasil Michev says:

      Truly automated solution is not possible yet, the workaround I use in the meantime is to add the IP (ranges) from which the given admin will be using PowerShell to the list of trusted/known locations in MFA/AAD settings.

      Reply
  20. Nikola Spasić says:

    Hi Vasil,

    I am trying to do something similar, but can’t figure it out. I have to get the html source of the portal.office.com web page, and was planning to use Invoke-WebRequest commandlet, but when I type:

    Invoke-WebRequest -Uri https://portal.office.com -UseBasicParsing

    I get the following response:

    StatusCode : 200
    StatusDescription : OK
    Content :

    Sign in to your account

    <meta http-eq…

    This is the Office365 login screen where I enter my credentials when I access from browser, but I can't figure out how to authenticate on that page from powershell. Basically, I don’t need access to Office365 apps, outlook, exchange or anything else, just to access portal like I’m accessing it from browser.

    Reply
    1. Vasil Michev says:

      The portal uses a different auth flow, I’m not aware of any way to automate getting a token for that.

      Reply
  21. Samraj says:

    HI Vasil,
    I have an Microsoft O365 account configured with ADFS in local. All the users are remains same as like AD account. BASIC credentials are blocked for Powershell access. I have an user who can able to access the Security and compliance center only.

    Now I am trying to authenticate the Powershell commandlets in unattended manner.

    -Credentials parameter is not working with Connect-EXOPSSession command. Connect-IPPSSession command working fine. But this command doesn’t have option to do it unattended manner. I am trying to run the Compliance command with Oauth Token ( MSo token / AD token available with me).

    Can you help me to run the compliance command with Bearer token?

    I have tried all the scenario and this is the most commonly used.

    $Password = ConvertTo-SecureString -AsPlainText $Authorization -Force
    $token = New-Object System.Management.Automation.PSCredential -ArgumentList “”, $Password

    I tried the above scenrio and I am getting below error.

    $Session = New-PSSession -ConfigurationName Microsoft.Exchange -ConnectionUri https://ps.compliance.protection.outlook.com -Credential $Ctoken -AllowRedirection
    WARNING: Your connection has been redirected to the following URI:
    “https://ps.compliance.protection.outlook.com/ucc/wsman ”
    New-PSSession : [ps.compliance.protection.outlook.com] Connecting to remote server
    ps.compliance.protection.outlook.com failed with the following error message : The WinRM client received an HTTP
    status code of 440 from the remote WS-Management service. For more information, see the about_Remote_Troubleshooting
    Help topic.
    At line:1 char:12
    + $Session = New-PSSession -ConfigurationName Microsoft.Exchange -Conne …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : OpenError: (System.Manageme….RemoteRunspace:RemoteRunspace) [New-PSSession], PSRemotin
    gTransportException
    + FullyQualifiedErrorId : -2144108273,PSSessionOpenFailed

    Reply
      1. Samraj says:

        Hi Michev, Now I can successfully connected with server .Thanks for your help.

        Earlier i used the command (Connect-IPPSSession) to get the access token from cache. Now I am trying to have Azure AD application within the tenant. I can”t able to provide the necessary permission to run the Security and compliance command with the generated access token. Can I know how to setup the Azure AD application to support security and compliance command with access token. Access token generated in above method is expired in 1 hour. So i need to setup one more app to generate the access token with refresh token.

        Reply
      2. Joni Snyder says:

        Vasil,

        I am trying to connect to Compliance PS using an account that is forced to MFA and having no luck using the commands above.

        Can you provide me with the commands/script to accomplish this?

        Many thanks!

        Reply
        1. Vasil Michev says:

          The method above works for the SCC cmdlets as well. Just change the corresponding connectionURI.

  22. Uma says:

    I am facing an error while executing lines to get access token.

    Cannot convert argument “ctx”, with value: “Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext”, for “AcquireTokenAsync” to type
    “Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext”: “Cannot convert the “Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext” value of type
    “Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext” to type “Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext”.”
    At line:5 char:1
    + $authResult = [Microsoft.IdentityModel.Clients.ActiveDirectory.Authen …
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    + CategoryInfo : NotSpecified: (:) [], MethodException
    + FullyQualifiedErrorId : MethodArgumentConversionInvalidCastArgument

    Reply
    1. Vasil Michev says:

      Keep in mind that this article is two years old by now and we have numerous updates to the ADAL binaries in the meantime. The example will work with the version found as part of the 2.0.0.98 Azure AD module, newer versions don’t even expose the UserPasswordCredential method anymore.

      Reply
      1. Uma says:

        Thank you for responding. I got this resolved. This error message was coming due to the clash between libraries from AzureAD and MSOnline. This particular DLL is found in both of these modules and I had loaded MSOnline one, which was causing the problem.
        But I have one more question related to client id. How can this id be same in other machines. . It also changed next day and the checked for the id in other machines for other user accounts. It is same for the day across machines. Could you please elaborate further on What is this client id exactly.

        Reply
  23. Mark Wickens says:

    I would like to have a user log into my web application and generate a token for use with Exchange Online (MFA) in PowerShell. This post has gotten me almost there. But how do I get an OAuth access token without an application secret? I’m new to OAuth, so I’m probably missing something obvious.

    Reply
    1. Vasil Michev says:

      That depends on the type of application you have. For a “Web App/API” type of application (private client), you can only use the secret, as it uses the so-called app permissions. For a “Native” app (public client), which use the ‘delegated permissions’ model, you can log the users with their own credentials. I’m probably not explaining it correctly as I’m far from being a programmer, but you can refer to the documentation on this, for example: https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-protocols-oauth-code

      Reply
      1. Mark Wickens says:

        Thanks. You’re right. For a native app, you don’t need the secret. For a web app (like mine), you do, unfortunately.

        I actually used Fiddler to see how Powershell was authenticating. Duplicating that method works in the Web app right up until it redirects to “urn:ietf:wg:oauth:2.0:oob” with the authorization code. Not helpful. :-/ And specifying a different redirect parameter doesn’t work, at least none that I’ve tried.

        Reply
  24. Mike says:

    This is working for me with ExO but I cannot figure out how to do it with a SharePoint Online instance where legacy auth is disabled. I’ve been tinkering with the connection string but now I am wondering: Is this even possible with this method?

    Reply
  25. Stuart says:

    Hi Vasil.

    I wanted to let you know that Microsoft accepted our DCR and have just released an update to the “Exchange Online (MFA module)” which now accepts passing credentials! I’ve tested and it works as expected. I was prompted to download the update when launching the “Microsoft Exchange Online Powershell Module” ClickOnce application. The Connect-EXOPSSession cmdlet now has a -Credential parameter.

    Reply
    1. Vasil Michev says:

      Thanks for sharing back Stuart, I do see the parameter now. And it seems to work just fine with bypassing MFA on trusted locations too!

      If you are forced to perform an MFA (or on untrusted location), you should not use it though:

      PS C:\Users\vasil> Connect-EXOPSSession -Credential $cred
      New-ExoPSSession : AADSTS50076: Due to a configuration change made by your administrator, or because you moved to a
      new location, you must use multi-factor authentication to access ‘00000002-0000-0ff1-ce00-000000000000’.

      Anyway, good addition, thank you guys for the DCR! 🙂

      Reply
      1. Nagamani says:

        Hi Vasil,

        i”m connecting O365 by passing Credentials —->
        Connect-EXOPSSession -Credential $Credential

        i’m getting below error , Please Help me…

        New-ExoPSSession : unknown_user_type: Unknown User Type nAt C:\Users\ilmhsqldbsvc\AppData\Local\Apps\2.0\D6ZQPBZQ.QYG\XNENGLCW.C6X\micr..tion_1975b8453054a2b5_0010.0000_10d85
        00
        8035862c6\CreateEXOPSSession.ps1:292 char:30
        + $PSSession = New-ExoPSSession -UserPrincipalName $UserPrincipalN …
        + ~~~~~~~~~
        + CategoryInfo : NotSpecified: (:) [New-ExoPSSession], AdalException
        + FullyQualifiedErrorId : Microsoft.IdentityModel.Clients.ActiveDirectory.AdalException,Microsoft.Exchange.Manag

        Reply
  26. Stuart says:

    Thanks Vasil. We’ve submitted a design change request to Microsoft to add support for passing local credentials to the Exchange Online MFA module. Hopefully they will take it in to consideration – especially given the other O365 online modules already have such support. Meanwhile your approach will allow us to configure automation scripts via Scheduled Tasks and service accounts! Thanks again.

    Reply
  27. Stuart says:

    Hi Vasil.

    This blog post is really helpful and useful. Thanks so much for posting – Microsoft should really document this stuff. Any ideas how to address that last issue that you pointed out… We’re only getting an access token, not a refresh token. And so when the session times-out it prompts for a password and does not reconnect. Is there anyway to overcome this? I am using the ADAL binaries from the Azure AD PowerShell module (2.0.0.131). Is there a way to check if the token has expired and refresh it?

    Reply
    1. Vasil Michev says:

      Afaik no. They simply removed the functionality to store/reuse refresh tokens when invoking the ADAL methods manually. Then again, I’m hardly an expert on ADAL, and I’m not even a programmer, so don’t take my word for it 🙂

      The token cache ($cache.ReadItems() in the examples above) will give you information about the currently stored tokens. If you do it manually, no refresh token is stored. If you use the built-in methods (for example if you use the ADAL-enabled ExO module), the token cache will store a refresh token as well.

      Reply

Leave a Reply

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

This site uses Akismet to reduce spam. Learn how your comment data is processed.