0

I'm setting up custom MFA providers for ADFS 3.0 by implementing IAuthenticationAdapter, I want to add another step in to the authentication process whereby, for example with email-based OTP, the user is first prompted to confirm his or her email address before the code is sent and then the user is prompted to enter the OTP, or perhaps the user is prompted to verify some mobile digits before sending out an SMS OTP, however the workflow in the authentication pipeline seems quite rigid, you get BeginAuthentication, OnError and TryEndAuthentication, but implementing additional steps seems much more involved.

So far I have some up with two possible solutions, and I'd like to hear if anyone as any comments or preferences, or hopefully a better way of doing this.

  1. Call TryEndAuthentication more than once, passing a different context to represent which stage of the process should be rendered with an IAdaperPresentation.

  2. Add jQuery and other custom scripts as text resources to the custom provider assembly and then inject them in to the IAdapterPresentationForm.GetFormHtml method to make the form dynamic and even do some in-line AJAX calls to a separate MVC / Web API service, and do the email verification before showing the user the authentication form, such that you end up with something like this...

Here's my custom script resource (Provider.txt):

            function alertMe() {
                $("#serviceResponse").html("It Works!");
            }

And here's what I did in GetFormHtml:

            var jQuery2 = Properties.Resources.jQuery2;
            var script = Properties.Resources.Provider;

            result += "<script type=\"text/javascript\">";
            result += jQuery2;
            result += "</script>";
            result += "<form method=\"post\" id=\"loginForm\" autocomplete=\"off\">";
            ...
            result += "    <input id=\"alertButton\" type=\"button\" name=\"Alert\" value=\"Alert Me\" onclick=\"alertMe();\" />";
            result += "    <div id=\"serviceResponse\" />";
            ...
            result += "    <input id=\"continueButton\" type=\"submit\" name=\"Continue\" value=\"Continue\" />";
            result += "</form>";
            result += "<script type=\"text/javascript\">";
            result += script;
            result += "</script>";

I've not tried the first method yet, and I'm not even sure if the ADFS authentication pipeline will allow this kind of workflow.

I have tried the second method and amazingly it does work, although, as I'm sure you'll agree that from a Developer's point-of-view it's nowhere near as clean as I'd like!

So what do you think, am I missing a simple setting or interface which I could cleanly implement to do this, or is this the only way?

Tom Tregenna
  • 1,281
  • 1
  • 13
  • 23
  • After working with the second method for a couple of days, it became far to cumbersome, so I have changed over to the first method - the workflow supports this quite happily - and it seems much better, plus I can do my processing server-side without having to resort to AJAX calls, and the cross-origin issues, etc. inherent to them. I'm now putting a 'pageId' in a hidden form field and switching on it when the workflow returns to the TryEndAuthentication method, then I either fire back the next step as an IAdaperPresentation, or null and populate the out claims when all the steps are completed. – Tom Tregenna Sep 25 '14 at 15:39
  • Yes, I experimented with this last spring. If there is a "Context" parameter in the POST data then it calls BeginAuthentication() If not then it calls TryEndAuthentication(). Which you can repeat by returning a new IAdapterPresentation. It ends on returning a null to TryEnAuthentication(). – paullem Sep 27 '14 at 09:37
  • Thanks paullem, although I've not seen the behaviour you describe where a context parameter causes BeginAuthentication to be re-called, especially since TryEndAuthentication also takes a context parameter. In my experience, after the initial call to BeginAuthentication, subsequent calls always go to TryEndAuthentication, until such time that null is returned. Am I doing something wrong? – Tom Tregenna Sep 30 '14 at 09:54
  • Hmmmmm. Rebuilding the test environment of last spring is a lot of work. Let's try it in a different way. Take a look (with your favorite disassembler) at Microsoft.IdentityServer.Web.Authentication.External.ExternalAuthenticationHandler.Process(). That is the method that will call your code. – paullem Sep 30 '14 at 10:17

0 Answers0