neos-sdi / adfsmfa

MFA for ADFS 2022/2019/2016/2012r2
MIT License
141 stars 52 forks source link

Proper way to register External SMS MFA provider class? #93

Closed astound-ts closed 4 years ago

astound-ts commented 4 years ago

What is the proper way to install custom MFA provider?

What has been done:

PS C:\temp\release> $myobj = New-Object("Neos.IdentityServer.MultiFactor.SMS.Astound.SMSCall"); PS C:\temp\release> $myobj| Get-Member

TypeName: Neos.IdentityServer.MultiFactor.SMS.Astound.SMSCall

Name MemberType Definition


Equals Method bool Equals(System.Object obj)
GetCodeWithExternalSystem Method Neos.IdentityServer.MultiFactor.AuthenticationResponseKind GetCodeWithExternalSystem(Neos.IdentityServer.MultiFactor.Registration reg, Neos.IdentityServer.MultiFactor.ExternalOTPProvide... ...



However, trying to add this MFA provided in MMC (via name "Neos.IdentityServer.Multifactor.SMS.Astound.SMSCall, Neos.IdentityServer.MultiFactor.SMS.Astound, Version=1.0.0.1, Culture=neutral, PublicKeyToken=793c8e6353e7e658") fails with "Invalid Extension ! cannot be loaded !" error.
Adding the same via PowerShell does not work as well.

Are there any extra steps needed?
redhook62 commented 4 years ago

Hi,

The External Provider (SMS) is just a sample working with Microsoft, This component is 10 years old, So, you Implement yourself this component... we don't know what is your bridge for SMS. please be more specific. Really, I find it hard to understand your worries

Regards

astound-ts commented 4 years ago

Hi, implementation is already done, and is not in scope of this question. The code is able to send SMS messages, check if these messages were sent, etc.

The question is why an assembly (DLL) loaded into the system and reachable from PowerShell, can not be used by adfsmfa. It does implement needed interface and was created from a sample provided with the code.

Maybe, MMC console should provide more detailed message on why it failed to load specified assembly?

redhook62 commented 4 years ago

Hi,

You can make a new extension when implementing some specific interfaces. IExternalOTPProvider (Legacy implementaion) an IExternalProvider interface and BaseExternalProvider abstract class)

You can Look at different samples

All using legacy interface IExternalOTPProvider

and

For registering your extension (GAC) you can follow documentation

You can do it with PowerShell or whit the MMC. Assembly must be fully qualified !

Regards

redhook62 commented 4 years ago

Hi @astound-ts

Sorry, there seems to be a very small bug when using legacy extensions ... It is true that we no longer really test this functionality, since there has been a new model for several years. We will quickly send an update. You can look at the new model (IExternalProvider) which is much more complete, your code will be only slightly affected

Regards

redhook62 commented 4 years ago

Hi,

Done, in version 2.4.4720.3003 Let us know if everything is OK

Regards

astound-ts commented 4 years ago

Hi, thanks - but it still does not work. Also, I think checking for null at L486 is pointless - as on line 490 we only do something if (prov != null)

Anyway, will try to re-implement SMS using modern interface.

redhook62 commented 4 years ago

Hi,

The modification is perfectly justified, The first line can return a null value, and in this case there is an error, it was otherwise the case.

I think you have another concern, it seems your DLL cannot be loaded.

Each time you save the config after modification, the list of providers is reloaded, so for example modifying a value in the console and saving will restart a loading of providers.

You can debug the solution, by installing the Remote debugger of VS 2017 on your ADFS server, the build is debuggable.

If you are going on the implementation of IExternalProvider, you can start from the following example: [](https://github.com/neos-sdi/adfsmfa/tree/master/Neos-IdentityServer 2.4/Neos.IdentityServer.MultiFactor.SMS.Azure)

Regards

Registering Legacy Provider in PS

$c = Get-MFAProvider -ProviderType External

$c.FullQualifiedImplementation = "Neos.IdentityServer.MultiFactor.Samples.ExternalLegacySample, Neos.IdentityServer.MultiFactor.Samples, Version=2.4.0.0, Culture=neutral, PublicKeyToken=175aa5ee756d2aa2"

$c.Parameters = $null

Set-MFAProvider -ProviderType External -Data $c

astound-ts commented 4 years ago

it seems your DLL cannot be loaded.

I've explicitly tried to load DLL in a PowerShell session:


# Load the assembly/DLL
PS C:\temp\release> [System.Reflection.Assembly]::Load("Neos.IdentityServer.MultiFactor.SMS.Astound, Version=1.0.0.3, Culture=neutral, PublicKeyToken=793c8e6353e7e658")

GAC    Version        Location                                                                                                                                                                             
---    -------        --------                                                                                                                                                                             
True   v4.0.30319     C:\Windows\Microsoft.Net\assembly\GAC_MSIL\Neos.IdentityServer.MultiFactor.SMS.Astound\v4.0_1.0.0.3__793c8e6353e7e658\Neos.IdentityServer.MultiFactor.SMS.Astound.dll                

# Load types from it
PS C:\temp\release> Add-Type -AssemblyName "Neos.IdentityServer.MultiFactor.SMS.Astound, Version=1.0.0.3, Culture=neutral, PublicKeyToken=793c8e6353e7e658"

# Instantiate a class
PS C:\temp\release> $instance = New-Object Neos.IdentityServer.MultiFactor.SMS.Astound.SMSCall

#Get members: DLL is loaded correctly
PS C:\temp\release> $instance | Get-Member

   TypeName: Neos.IdentityServer.MultiFactor.SMS.Astound.SMSCall

Name                          MemberType Definition                                                                                                                                                        
----                          ---------- ----------                                                                                                                                                        
Equals                        Method     bool Equals(System.Object obj)                                                                                                                                    
GetCodeWithExternalSystem     Method     Neos.IdentityServer.MultiFactor.AuthenticationResponseKind GetCodeWithExternalSystem(Neos.IdentityServer.MultiFactor.Registration reg, Neos.IdentityServer.Mult...
GetHashCode                   Method     int GetHashCode()                                                                                                                                                 
GetType                       Method     type GetType()                                                                                                                                                    
GetUserCodeWithExternalSystem Method     int GetUserCodeWithExternalSystem(string upn, string phonenumber, string smstext, Neos.IdentityServer.MultiFactor.ExternalOTPProvider externalsys, cultureinfo ...
ToString                      Method     string ToString()                                                                                                                                                 

Is it OK that PublicKeyToken=793c8e6353e7e658 I've used is different from the one entire ADFSmfa solution is signed? It is obviously different since I don't have your private keys (and for a good reason)

redhook62 commented 4 years ago

Hi,

The sample in this thread works as well.

Have you debugged ? Keep in mind, as it's specified in Remarks on the main page, that your assemblies MUST be signed with a .pfx certificate. not a snk key ?

Regards

astound-ts commented 4 years ago

Decided to recompile whole solution and install resulting MSI (with assemblies signed with "my" key). As a result, everything works with custom MFA provider (legacy implementation)

So it looks like the root cause was that PFX-signed assemblies signed by different keys can not co-exist and load each other.

The following needs to be added to documentation: If custom MFA plugin is developed, whole solution needs to be recompiled, as MSI packages from https://github.com/neos-sdi/adfsmfa/releases will be unable to load custom-built extensions.

redhook62 commented 4 years ago

Hi,

You can easily recompile the solution, after all we are on GitHub, so it is code sharing. However, I would like you to listen to my remarks to you.

Your assembly must imperatively be signed in visual Studio 2017 with a signature file which is a certificate integrating a private key (Strong Signing), it is some of required to load extensions in the ADFS process.

For your information, I had no doubts, I recompiled the demo project and signed it with a new key ... and of course! it works.

I am enclosing a certificate whose password is the name of the pfx file (case sensitive). Assign this certificate to your project as a signature. it must work, if not, the problem is elsewhere

Merry christmas

The pfx is added in relaese files (2.4)