Skip to content
This repository has been archived by the owner on Jun 30, 2023. It is now read-only.

CreateAuthenticationDialog - The method or operation is not implemented. #879

Closed
markdwags opened this issue Oct 25, 2017 · 10 comments
Closed

Comments

@markdwags
Copy link

I'm attempting to acquire a token from ADFS 3.0 using OAuth2 using .NET Core. Using .NET 4.5, I have no issues because of a combination of PromptBehavior.Never and it automatically handling in the passing in the credentials of the Windows user who is logged in.

In .NET Core, that isn't available (long term this will run from an AWS Lambda function) but I'm unable to find a way to package in the username/password that has permission to auth to get the token to put into the Authorization header in the HttpClient in a way that works with CRM 2016.

ADFS 3.0 OAuth2 with the ADFS Client is very specific, and requires that you pass in a resource (which is the ADFS defined URL), the client ID (created on the ADFS server with Add-AdfsClient), and the redirect URL (also defined using Add-AdfsClient).

string resource = "https://site.url.com/";
string clientId = "11111111-1111-1111-1111-11111111111";
string redirectUrl = "https://site.url.com/CRM/";

AuthenticationContext authContext = new AuthenticationContext("https://adfs.domain.com/adfs/oauth2", false);

var platformParams = new PlatformParameters();

var authResult = await authContext.AcquireTokenAsync(resource, clientId, new Uri(redirectUrl), platformParams);

It's kicking out this error.

{System.NotImplementedException: The method or operation is not implemented.
   at Microsoft.IdentityModel.Clients.ActiveDirectory.WebUIFactory.CreateAuthenticationDialog(IPlatformParameters parameters) at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.<AcquireTokenCommonAsync>d__39.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationContext.<AcquireTokenAsync>d__29.MoveNext()
--- End of stack trace from previous location where exception was thrown ---
   at System.Runtime.ExceptionServices.ExceptionDispatchInfo.Throw()
   at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task)
   at System.Runtime.CompilerServices.TaskAwaiter`1.GetResult()
   at CRMTestCore.Program.<TestNewTokenWay>d__2.MoveNext() in C:\Projects\CRMTestCore\CRMTestCore\CRMTestCore\Program.cs:line 140}	System.Exception {System.NotImplementedException}

Essentially, I need a way to bypass this auth dialog and input my own domain, username and password that it will use to authenticate to get the token.

Thanks!

@kpanwar
Copy link
Contributor

kpanwar commented Oct 25, 2017

We don’t support username password based flows in .net core. But at the same time adfs v3 does not support username password based authentication (resource owner flow) either.

@markdwags
Copy link
Author

Regarding username/password, I was referring to this flow.

For example, on Windows using .NET 4.5, the following code will use the credentials of the logged in user automatically.

string resource = "https://site.url.com/";
string clientId = "11111111-1111-1111-1111-11111111111";
string redirectUrl = "https://site.url.com/CRM/";

AuthenticationContext authContext = new AuthenticationContext("https://adfs.domain.com/adfs/oauth2", false);

AuthenticationResult authResult = authContext.AcquireToken(resource, clientId, new Uri(redirectUrl), PromptBehavior.Never);

If I were to use PromptBehavior.Always for example, I get the dialog prompt where I can enter any domain credentials for ADFS to use to authenticate against AD and assuming authentication is successful, authRequest has a token I can use for the HttpClient.

I'm able to package in domain credentials using the HttpClientHandler in .NET Core (so I have the ability to run in Linux)

 HttpClientHandler _httpClientHandler = new HttpClientHandler()
{
        Credentials = new NetworkCredential("aduser", "adpassword", "domain.tld"),                  
};

I was inquiring (and hoping!) this library would support something similar.

@angshuman-agarwal
Copy link

angshuman-agarwal commented Apr 16, 2018

string resource = "https://site.url.com/";
string clientId = "11111111-1111-1111-1111-11111111111";
string redirectUrl = "https://site.url.com/CRM/";

AuthenticationContext authContext = new AuthenticationContext("https://adfs.domain.com/adfs/oauth2", false);

AuthenticationResult authResult = authContext.AcquireToken(resource, clientId, new Uri(redirectUrl), PromptBehavior.Never);

I do not believe above will work with PromptBehavior.Never because the very 1st time , if token is not present in cache, it will flash up a web browser and PromptBehavior.Never will fail.

Please read Note that: section here which says :

If you use PromptBehavior = Never and the token does not exist in the cache, or needs to be refreshed with UI, the call to AcquireTokenAsync will fail with an exception of type AdalException

@jmprieur
Copy link
Contributor

markdwags, the flow you describe above is an interactive flow which is not supported in .NET core as .NET core does not offer a Web browser

Indeed @angshuman-agarwal, indeed one should what is described in recommended pattern to acquire a token in public client application. you should first try the cache, and if this fail you try the interaction.

Also see this summary for which platform supports which flow: summary for public client applications

__
closing this issue as by design as .NET Core does not support UI yet. We will implement interactive flows in .NET Core when it does.

@angshuman-agarwal
Copy link

angshuman-agarwal commented Apr 16, 2018

@jmprieur Yes ; but that poses an issue. I am not able to make my app seamless because in PrompBehavior.Auto, I get an empty Browser window till token is returned (although user does not need to authenticate). I cannot make it PromptBehavior.Never because 1st time token in not present in cache. Any workaround for this pls ?

Can we make the browser hidden by passing an optional parameter to PlatformParameters?

Something like :
new PlatformParameters(PromptBehavior.Auto, null, false);

@jmprieur
Copy link
Contributor

@angshuman-agarwal if you use systematically AcuireTokenSilentAsync before your call to AcquireTokenAsync( with Auto), then you will not see any browser.

@angshuman-agarwal
Copy link

angshuman-agarwal commented Apr 16, 2018

@jmprieur

Sorry, that is not true.

I followed this pattern here where I call AcuireTokenSilentAsync 1st and then in catch I will have to call AcquireTokenAsyc as very 1st time, token will not be found silently and always requires Interaction.
And during this Interaction, I see the Webbrowser blank dialog.

try
{
 result = await ac.AcquireTokenSilentAsync(resource, clientId);
}
catch (AdalException adalException) // 1st time this will happen  in any case
{
 if (adalException.ErrorCode == AdalError.FailedToAcquireTokenSilently
     || adalException.ErrorCode == AdalError.InteractionRequired)
  {
   result = await ac.AcquireTokenAsync(resource, clientId,redirectUri,
                                       new PlatformParameters(PromptBehavior.Auto)); //----> I want to hide the browser here by passing optional parameter false
  }
}

Please also read the note here for PromptBehavior.Auto-

If no token exists in the cache, but the user is known, or a session cookie is available in the web browser control used for the interaction, the dialog flashes but disappears immediately.

So very 1st call , I will always see the Browser

@jmprieur
Copy link
Contributor

@angshuman-agarwal. I'm confused about what you want to achieve

This is an interactive flow. so the very first time the user uses the application, s/he will have to sign-in interactively. Then, provided you have implemented a token cache serialization (only needed on .NET Framework and .NET Core), you won't have to re-sign in the user.

Now, if you are in an enterprise scenario, with the user's machine domain or AAD joint, you'd want to use the Windows integrated authentication flow, which does not show a browser (but that was not my understanding from the thread above. Sorry if I missed something)

@markdwags
Copy link
Author

markdwags commented Apr 16, 2018

@jmprieur

I'm trying to get past the part that requires someone to sign in interactively -- where it would use an Active Directory username and password (stored in a config or obtained some other way) that I pass in and it allows me to bypass the interactive window and use the credentials that way,

Specifically, my use-case is attempting to get this token from an AWS Lambda function and then using that token to interact with the CRM Web API with that token.

Unfortunately, my "work around" has been to have a Windows Service installed that gets that token every minute, then pushes the value to AWS Lambda so the function can use the token. I'd love to not have this extra service, instead allow the Lambda function to obtain this token.

@jmprieur
Copy link
Contributor

@markdwags : thanks for sharing your scenario.
I'm still confused on how this relate to the title of this github issue, though.

Did you try to use the Username/password flow?

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

4 participants