C# ADAL AcquireTokenAsync() without pop-up box
Asked Answered
A

3

7

We are writing a WCF service which has to integrate with Dynamics CRM 2016 Online. I'm trying to authenticate using ADAL, using method AcquireTokenAsync(). Problem is, it displays a pop-up box, prompting the user for credentials. Naturally, our application being a service, this isn't what we want. We've been searching for a way to authenticate without having this pop-up box.

There is a class called AuthenticationContextIntegratedAuthExtensions, which is supposed to assist with "username/password flow". It has the single method AcquireTokenAsync, which suppresses the pop-up box, but we haven't found any way to pass the password to it. When run with just the username, it raises the exception that basically says "no password was supplied".

Does anyone have any idea how to work around this? Doesn't even have to be ADAL. Just something to acquire the OAuth token.

Albumenize answered 24/6, 2016 at 9:52 Comment(0)
S
4
private static string API_BASE_URL = "https://<CRM DOMAIN>.com/";
private static string API_URL = "https://<CRM DOMAIN>.com/api/data/v8.1/";
private static string CLIENT_ID = "<CLIENT ID>";

static void Main(string[] args)
{
    var ap = AuthenticationParameters.CreateFromResourceUrlAsync(
                new Uri(API_URL)).Result;

    var authContext = new AuthenticationContext(ap.Authority, false);

    var userCredential = new UserCredential("<USERNAME>", "<PASSWORD>");

    var result = authContext.AcquireToken(API_BASE_URL, CLIENT_ID, userCredential);

    var httpClient = HttpWebRequest.CreateHttp(Path.Combine(API_URL, "accounts"));
    httpClient.Headers.Add(HttpRequestHeader.Authorization, "Bearer:" + result.AccessToken);
    using (var sr = new StreamReader(httpClient.GetResponse().GetResponseStream()))
    {
        Console.WriteLine(sr.ReadToEnd());
    }
}

Note: I'm using an older version of ADAL (2.19.208020213) as it appears the password parameter has been taken out of the UserCredential constructor.

EDIT: Latest versions of ADAL have UserPasswordCredential which can be used in place of UserCredential (and probably was added as soon as Password was removed from UserCredential)

EDIT 2: CRM now supports Server to Server Authentication which allows you to create an application user.

Spracklen answered 24/6, 2016 at 16:58 Comment(3)
Yes. The password field was taken out for whatever reason. In fact, we have failed to find any class or method that has anything to do with passwords. Some classes have a constructor that accept a username, UserIdentifier, for one. Though it's purpose appears to be auto-complete, rather than any attempt at silent authentication.Albumenize
Right, i'm not sure why it was removed either (this blog cloudidentity.com/blog/2014/07/08/… is the one I remember reading when it was first introduced), but if you use the specific version above you can authenticate without a popup and the token will work for requests against CRM.Spracklen
Seems like we'll have to use your solution. The one I posted here doesn't let us access the CRM, giving 401 errors.Albumenize
A
1

For what it's worth without using ADAL

var postData = new List<KeyValuePair<string, string>>
        {
            new KeyValuePair<string, string>("resource", cred.ResourceId),
            new KeyValuePair<string, string>("grant_type", "client_credentials"),
            new KeyValuePair<string, string>("client_id", cred.ClientId),
            new KeyValuePair<string, string>("client_secret", cred.ClientSecret),

        };

        using (var client = new HttpClient()) {

            string baseUrl = "https://login.windows.net/YourAADInstanceName.onmicrosoft.com/oauth2/";
            client.BaseAddress = new Uri(baseUrl);                
            client.DefaultRequestHeaders.Accept.Clear();
            client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

            var content = new FormUrlEncodedContent(postData);

            HttpResponseMessage response = await client.PostAsync("token", content);               
            string jsonString = await response.Content.ReadAsStringAsync();                
            var responseData = JsonConvert.DeserializeObject<Token>(jsonString);
            return responseData;
        }
Accoucheur answered 15/9, 2016 at 13:48 Comment(1)
This authenticates and returns an accessToken but when I use that token it gives a 401.Adytum
A
0

Okay, ended up finding a solution for this.

Provided you have registered your application with Azure AD (as a Web App / Web API, not a Native Application), you will receive a client ID and a secret key for that application.

The code to acquire the token without having a pop-up window is as follows:

AuthenticationParameters ap = AuthenticationParameters.CreateFromResourceUrlAsync(
                        new Uri(resource+"/api/data/v8.1")).Result;

AuthenticationContext ac = new AuthenticationContext(ap.Authority);

AuthenticationResult r = await ac.AcquireTokenAsync(ap.Resource, new ClientCredential(clientId,clientSecret));

resource is the base URL of your Dynamics CRM deployment.

Authentication parameters are discovered at run time as suggested in this MSDN acticle.

Albumenize answered 24/6, 2016 at 15:36 Comment(5)
Did you try using this token to make a call against CRM's webapi? I'd be curious if the token you have will work when passed along to CRM since no user was specified. If it does work who are the calls being made as in CRM?Spracklen
Not yet. However, when you register the application in Azure AD, you can grant it permission to Dynamics CRM. Not any specific CRM, mind you, so I'd assume you get access to any instance that is deployed on that Azure AD. And the AuthenticationParameters are acquired by pinging our CRM instance, which we then use to authenticate against the underlying AD by passing AuthenticationParameters.Resource to AcquireTokenAsync().Albumenize
But you aren't authenticated as a user, CRM allows "delegated permissions" and not "application permissions" meaning you need a signed in user. When you provide this token to CRM you're probably going to get a 401.Spracklen
Yes, not authenticated as any specific user. I'll be writing some queries to CRM today and see if it complains about it.Albumenize
@MattDearing You were correct. Gives me 401 errors. Guess we'll have to pass in the user details.Albumenize

© 2022 - 2024 — McMap. All rights reserved.