Azure/azure-powershell

Connect-AzureRmAccount : Remove Credential parameter from User login parameter set

Closed this issue · 12 comments

Description

Login-AzureRMAccount when using PowerShell Core and AzureRM.Netcore cannot accept PSCredentials.
Using interactive login via browser works fine however.

PS /> Login-AzureRmAccount -Credential $AZScred -Verbose
VERBOSE: Performing the operation "log in" on target "User account in environment 'AzureCloud'".
Login-AzureRmAccount : password_required_for_managed_user: Password is required for managed user
At line:1 char:1
+ Login-AzureRmAccount -Credential $AZScred -Verbose
+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : CloseError: (:) [Add-AzureRmAccount], AadAuthenticationFailedException
+ FullyQualifiedErrorId : Microsoft.Azure.Commands.Profile.AddAzureRMAccountCommand

Script/Steps for Reproduction

It was run from Docker: docker run -ti microsoft/powershell

Install-Module AzureRM.NetCore -Force

Import-Module AzureRM.Netcore
Import-Module AzureRM.Profile.Netcore

Add-AzureRMEnvironment -Name "AzureStackUser" -ArmEndpoint "https://management.x.x"
# Sign in to your environment
# Create your Credentials
$AZSusername =  "Admin@x.onmicrosoft.com"
$AZSpassword = 'Password123'
  $AZSuserPassword = ConvertTo-SecureString "$AZSpassword" -AsPlainText -Force
  $AZScred = new-object -typename System.Management.Automation.PSCredential -argumentlist $AZSusername,$AZSuserPassword
Login-AzureRmAccount -Credential $AZScred -EnvironmentName "AzureStackUser"

Module Version

PS /> get-module

ModuleType Version    Name                                ExportedCommands
---------- -------    ----                                ----------------
Manifest   0.10.0     AzureRM.Netcore
Script     0.10.0     AzureRM.Profile.Netcore             {Add-AzureRmAccount, Add-AzureRmEnvironment, Clear-AzureR...
Manifest   3.1.0.0    Microsoft.PowerShell.Management     {Add-Content, Clear-Content, Clear-Item, Clear-ItemProper...
Manifest   3.0.0.0    Microsoft.PowerShell.Security       {ConvertFrom-SecureString, ConvertTo-SecureString, Get-Cr...
Manifest   3.1.0.0    Microsoft.PowerShell.Utility        {Add-Member, Add-Type, Clear-Variable, Compare-Object...}
Script     1.1.7.0    PackageManagement                   {Find-Package, Find-PackageProvider, Get-Package, Get-Pac...
Script     1.6.0      PowerShellGet                       {Find-Command, Find-DscResource, Find-Module, Find-RoleCa...
Script     1.2        PSReadLine                          {Get-PSReadlineKeyHandler, Get-PSReadlineOption, Remove-P...

Environment Data

PS /> $PSVersionTable

Name                           Value
----                           -----
PSVersion                      6.0.2
PSEdition                      Core
GitCommitId                    v6.0.2
OS                             Linux 4.9.60-linuxkit-aufs #1 SMP Mon Nov 6 16:00:12 UTC 2017
Platform                       Unix
PSCompatibleVersions           {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion      2.3
SerializationVersion           1.1.0.1
WSManStackVersion              3.0

@MiYanni can you look at this issue?

@cblackuk This will be a bit before I investigate specific netcore issues. I'm nearly done getting the rest of the module projects building properly in netcore. So, I'll be doing a lot of testing in the next few weeks. I'll make sure to check this issue during that timeframe.

@MiYanni Thanks a lot! Really appreciate, please keep us posted :)

This is by design. Usename + Password authentication is not supported in NetCore - you must use device login or service princpal login

@markcowl If that is true, we shouldn't expose the -Credential parameter in NetCore.

@MiYanni -Credential is used with Service Principal as well as User Principal login. We should likely remove Credential from user parameter set, but the parameter itself will remain.

@MiYanni @markcowl
As I am sure you know the issue here actually is with ActiveDirectory DLL that does not support Password in its class on .NETCore but that is only the beginning of the problem... See below:

What I mean by that is:

docker run -ti azuresdk/azure-powershell-core

### It wants to load the dll from NetCoreModule
$adal = "/usr/local/share/powershell/Modules/AzureRM.Profile.Netcore/Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
[System.Reflection.Assembly]::LoadFrom($adal)


Function GetAuthToken
    {
    param
        (
        [Parameter(Mandatory=$true)]
        $TenantName
        )
    Import-Module Azure
    $clientId = "1950a258-227b-4e31-a9cf-717495945fc2" 
    $redirectUri = "urn:ietf:wg:oauth:2.0:oob"
    $resourceAppIdURI = "https://graph.microsoft.com"
    $authority = "https://login.microsoftonline.com/$TenantName"
    $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
    $Credential = Get-Credential
    $AADCredential = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential" -ArgumentList $credential.UserName,$credential.Password
    $authResult = $authContext.AcquireToken($resourceAppIdURI, $clientId,$AADCredential)
    return $authResult
    }

If you just take this:

PS />   $AADCredential = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential"
PS /> $AADCredential

UserName
--------



PS /> $AADCredential.
UserName     Equals       GetHashCode  GetType      ToString
PS /> $AADCredential.
UserName     Equals       GetHashCode  GetType      ToString
PS /> $AADCredential | gm


   TypeName: Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential

Name        MemberType Definition
----        ---------- ----------
Equals      Method     bool Equals(System.Object obj)
GetHashCode Method     int GetHashCode()
GetType     Method     type GetType()
ToString    Method     string ToString()
UserName    Property   string UserName {get;}

PS />   $AADCredential = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential" -ArgumentList user,
password
New-Object : Cannot find an overload for "UserCredential" and the argument count: "2".
At line:1 char:20
+ ... redential = New-Object "Microsoft.IdentityModel.Clients.ActiveDirecto ...
+                 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : InvalidOperation: (:) [New-Object], MethodException
+ FullyQualifiedErrorId : ConstructorInvokedThrowException,Microsoft.PowerShell.Commands.NewObjectCommand

You can see above that it does not allow second Argument to be passed in the class/object definition.

Therefore, I did the following (thinking basically to import the same DLL that works on Desktop PowerShell to Core to see if it will work)

Save-Module -Name Microsoft.ADAL.PowerShell -Path /tmp
$adal = "/tmp/Microsoft.ADAL.PowerShell/1.12/Microsoft.IdentityModel.Clients.ActiveDirectory.dll"
[System.Reflection.Assembly]::LoadFrom($adal)

GAC    Version        Location
---    -------        --------
False  v4.0.30319     /tmp/Microsoft.ADAL.PowerShell/1.12/Microsoft.IdentityModel.Clients.ActiveDirectory.dll

$AADCredential = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential" -ArgumentList user,password
PS /> $AADCredential

UserName
--------
user

This shows that we can now pass second Argument.

So let's try to pass it correctly:

PS />     $clientId = "1950a258-227b-4e31-a9cf-717495945fc2"
PS />     $redirectUri = "urn:ietf:wg:oauth:2.0:oob"
PS />     $resourceAppIdURI = "https://graph.microsoft.com"
PS />     $authority = "https://login.microsoftonline.com/common"
PS />     $authContext = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext" -ArgumentList $authority
PS />     $Credential = Get-Credential

PowerShell credential request
Enter your credentials.
User: Admin@x.onmicrosoft.com
Password for user Admin@x.onmicrosoft.com: ***********

PS />     $AADCredential = New-Object "Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential" -ArgumentList $credential.UserName,$credential.Password
PS />     $authResult = $authContext.AcquireToken($resourceAppIdURI, $clientId,$AADCredential)
Exception calling "AcquireToken" with "3" argument(s): "Could not load type 'System.Security.Cryptography.SHA256Cng' from assembly 'System.Core, Version=4.0.0.0, Culture=ne
utral, PublicKeyToken=b77a5c561934e089'."
At line:1 char:5
+     $authResult = $authContext.AcquireToken($resourceAppIdURI, $clien ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : TypeLoadException

For clarity Actual Error is:

PS />     $authResult = $authContext.AcquireToken($resourceAppIdURI, $clientId,$AADCredential)
Exception calling "AcquireToken" with "3" argument(s): "Could not load type 'System.Security.Cryptography.SHA256Cng' from assembly 'System.Core, Version=4.0.0.0, Culture=ne
utral, PublicKeyToken=b77a5c561934e089'."
At line:1 char:5
+     $authResult = $authContext.AcquireToken($resourceAppIdURI, $clien ...
+     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ CategoryInfo          : NotSpecified: (:) [], MethodInvocationException
+ FullyQualifiedErrorId : TypeLoadException

When you use https://www.jetbrains.com/decompiler/ to open DLLs. you can see first of all confirmation of the Arguments not being created as Desktop AD DLL has got additional overload:

// Decompiled with JetBrains decompiler
// Type: Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential
// Assembly: Microsoft.IdentityModel.Clients.ActiveDirectory, Version=2.19.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35
// MVID: C5333A68-57A4-4EFC-A3FA-EB33FD01628E
// Assembly location: C:\Temp\Microsoft.ADAL.PowerShell\1.12\Microsoft.IdentityModel.Clients.ActiveDirectory.dll

using System.Security;

namespace Microsoft.IdentityModel.Clients.ActiveDirectory
{
  public sealed class UserCredential
  {
    public UserCredential()
    {
      this.UserAuthType = UserAuthType.IntegratedAuth;
    }

    public UserCredential(string userName)
    {
      this.UserName = userName;
      this.UserAuthType = UserAuthType.IntegratedAuth;
    }

    public string UserName { get; internal set; }

    internal UserAuthType UserAuthType { get; private set; }

    public UserCredential(string userName, string password)
    {
      this.UserName = userName;
      this.Password = password;
      this.UserAuthType = UserAuthType.UsernamePassword;
    }

    public UserCredential(string userName, SecureString securePassword)
    {
      this.UserName = userName;
      this.SecurePassword = securePassword;
      this.UserAuthType = UserAuthType.UsernamePassword;
    }

    internal string Password { get; private set; }

    internal SecureString SecurePassword { get; private set; }

    internal char[] PasswordToCharArray()
    {
      if (this.SecurePassword != null)
        return this.SecurePassword.ToCharArray();
      if (this.Password == null)
        return (char[]) null;
      return this.Password.ToCharArray();
    }
  }
}

Basically this:

 public UserCredential(string userName, SecureString securePassword)

Sadly when this gets invoked C# is trying to load System.Core DLL. (mind you on NetCore version of that DLL the whole securePassword is not even listed at all inside that UserCredential Class)

This is caused by System.Core DLL not being able to load SHA256cng because this is trying to load SHA256

// Decompiled with JetBrains decompiler
// Type: System.Security.Cryptography.SHA256Cng
// Assembly: System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089
// MVID: 3B8EC409-2940-4CA6-85B7-E766485A6809
// Assembly location: C:\Windows\Microsoft.NET\Framework64\v4.0.30319\System.Core.dll

using System.Security.Permissions;

namespace System.Security.Cryptography
{
  [HostProtection(SecurityAction.LinkDemand, MayLeakOnAbort = true)]
  public sealed class SHA256Cng : SHA256

In turn SHA256 wants to use HashAlgorythm and all of those are in mscorlib.dll but in DotNet they do not work correctly as stated here:

http://support372.rssing.com/chan-9410057/all_p2.html
http://support372.rssing.com/browser.php?indx=9410057&item=23

If you are using .Net Framework 4.0 then the resolution is to modify the “machine.config” file at:

%WINDIR%\Microsoft.NET\Framework\v4.0.xxxxx\ CONFIG  -> for x86
%WINDIR%\Microsoft.NET\Framework64\v4.0.xxxxx\CONFIG -> for x64

If you are using .Net Framework 3.5 then the resolution is to modify the “machine.config” file at:

%WINDIR%\Microsoft.NET\Framework\v2.0.xxxxx\ CONFIG  -> for x86
%WINDIR%\Microsoft.NET\Framework64\v2.0.xxxxx\CONFIG -> for x64

Here is the entry that you need to make at the "machine.config" file for supporting SHA256CryptoServiceProvider, SHA256Cng, SHA384CryptoServiceProvider, SHA384Cng, SHA512CryptoServiceProvider, and SHA512Cng.

  <mscorlib>
    <cryptographySettings>
      <cryptoNameMapping>
        <cryptoClasses>
          <cryptoClass SHA256CSP="System.Security.Cryptography.SHA256CryptoServiceProvider, System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
          <cryptoClass SHA256CNG="System.Security.Cryptography.SHA256Cng, System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
          <cryptoClass SHA384CSP="System.Security.Cryptography.SHA384CryptoServiceProvider, System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
          <cryptoClass SHA384CNG="System.Security.Cryptography.SHA384Cng, System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
          <cryptoClass SHA512CSP="System.Security.Cryptography.SHA512CryptoServiceProvider, System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
          <cryptoClass SHA512CNG="System.Security.Cryptography.SHA512Cng, System.Core, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
        </cryptoClasses>
        <nameEntry name="SHA256" class="SHA256CSP" />
        <nameEntry name="SHA256CryptoServiceProvider" class="SHA256CSP" />
        <nameEntry name="System.Security.Cryptography.SHA256CryptoServiceProvider" class="SHA256CSP" />
        <nameEntry name="SHA256Next" class="SHA256CNG" />
        <nameEntry name="SHA256Cng" class="SHA256CNG" />
        <nameEntry name="System.Security.Cryptography.SHA256Cng" class="SHA256CNG" />
        <nameEntry name="SHA384" class="SHA384CSP" />
        <nameEntry name="SHA384CryptoServiceProvider" class="SHA384CSP" />
        <nameEntry name="System.Security.Cryptography.SHA384CryptoServiceProvider" class="SHA384CSP" />
        <nameEntry name="SHA384Next" class="SHA384CNG" />
        <nameEntry name="SHA384Cng" class="SHA384CNG" />
        <nameEntry name="System.Security.Cryptography.SHA384Cng" class="SHA384CNG" />
        <nameEntry name="SHA512" class="SHA512CSP" />
        <nameEntry name="SHA512CryptoServiceProvider" class="SHA512CSP" />
        <nameEntry name="System.Security.Cryptography.SHA512CryptoServiceProvider" class="SHA512CSP" />
        <nameEntry name="SHA512Next" class="SHA512CNG" />
        <nameEntry name="SHA512Cng" class="SHA512CNG" />
        <nameEntry name="System.Security.Cryptography.SHA512Cng" class="SHA512CNG" />
      </cryptoNameMapping>
      <oidMap>
        <oidEntry OID="2.16.840.1.101.3.4.2.1" name="SHA256" />
        <oidEntry OID="2.16.840.1.101.3.4.2.1" name="SHA256Next" />
        <oidEntry OID="2.16.840.1.101.3.4.2.2" name="SHA384" />
        <oidEntry OID="2.16.840.1.101.3.4.2.2" name="SHA384Next" />
        <oidEntry OID="2.16.840.1.101.3.4.2.3" name="SHA512" />
        <oidEntry OID="2.16.840.1.101.3.4.2.3" name="SHA512Next" />
      </oidMap>
    </cryptographySettings>
  </mscorlib> 

And here: https://help.relativity.com/9.2/Content/Installing_and_Upgrading/Relativity_installation.htm

Use this procedure to update the machine.config file:

    Log in to your agent or web server.
    Navigate to the machine.config file in the following directory:

    C:\Windows\Microsoft.NET\Framework64\v4.0.30319\Config
    Open the machine.config file in a text editor, and add the following code after the </system.web> element:
<mscorlib>
    <cryptographySettings>
        <cryptoNameMapping>
            <cryptoClasses>
                <cryptoClass SHA256CNG="System.Security.Cryptography.SHA256Cng, System.Core, Version=4.0.0.0, Culture=neutral, 
                      PublicKeyToken=b77a5c561934e089" />
                <cryptoClass SHA512CNG="System.Security.Cryptography.SHA512Cng, System.Core, Version=4.0.0.0, Culture=neutral,
                      PublicKeyToken=b77a5c561934e089" />
            </cryptoClasses>
                <nameEntry name="SHA256" class="SHA256CNG" />
                <nameEntry name="SHA-256" class="SHA256CNG" />
                <nameEntry name="System.Security.Cryptography.SHA256" class="SHA256CNG" />
                <nameEntry name="SHA512" class="SHA512CNG" />
                <nameEntry name="SHA-512" class="SHA512CNG" />
                <nameEntry name="System.Security.Cryptography.SHA512" class="SHA512CNG" />
                <nameEntry name="SHA256Managed" class="SHA256CNG" />
                <nameEntry name="System.Security.Cryptography.SHA256Managed" class="SHA256CNG" />
                <nameEntry name="SHA512Managed" class="SHA512CNG" />
                <nameEntry name="System.Security.Cryptography.SHA512Managed" class="SHA512CNG" />
        </cryptoNameMapping>
    </cryptographySettings>
</mscorlib>

It looks to me like the issue itself is in mscorlib in NetCore not in Azure or PowerShell per se.
I do not know enough C# or .Net to fix it but it looks like you can work around it on Windows but I am not able to find workaround in Linux.

Additional Overload definition from Desktop Library:

PS /> $authContext.AcquireToken

OverloadDefinitions
-------------------
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult AcquireToken(string resource, string clientId, Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential userCredential)
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult AcquireToken(string resource, string clientId, Microsoft.IdentityModel.Clients.ActiveDirectory.UserAssertion userAssertion)
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult AcquireToken(string resource, Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential clientCredential)
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult AcquireToken(string resource, Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate clientCertificate)
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult AcquireToken(string resource, Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertion clientAssertion)
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult AcquireToken(string resource, string clientId, uri redirectUri)
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult AcquireToken(string resource, string clientId, uri redirectUri, Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior promptBehavior)
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult AcquireToken(string resource, string clientId, uri redirectUri, Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior promptBehavior, Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier userId)
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult AcquireToken(string resource, string clientId, uri redirectUri, Microsoft.IdentityModel.Clients.ActiveDirectory.PromptBehavior promptBehavior, Microsoft.IdentityModel.Clients.ActiveDirectory.UserIdentifier userId, string extraQueryParameters)
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult AcquireToken(string resource, Microsoft.IdentityModel.Clients.ActiveDirectory.ClientCredential clientCredential, Microsoft.IdentityModel.Clients.ActiveDirectory.UserAssertion userAssertion)
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult AcquireToken(string resource, Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertionCertificate clientCertificate, Microsoft.IdentityModel.Clients.ActiveDirectory.UserAssertion userAssertion)
Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult AcquireToken(string resource, Microsoft.IdentityModel.Clients.ActiveDirectory.ClientAssertion clientAssertion, Microsoft.IdentityModel.Clients.ActiveDirectory.UserAssertion userAssertion)

This is the one we use I think:

Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationResult AcquireToken(string resource, string clientId, Microsoft.IdentityModel.Clients.ActiveDirectory.UserCredential userCredential)

Does that help?

@henrik-me Which I hope I explained why above ;-)

@cblackuk you did, thanks for the detailed explanation. I just wanted to point directly to the source.

@cblackuk @henrik-me -Credential is removed from the User parameter set (not Service Principal set) for NetCore in 0.12.0