5

I have a console application registered in Azure AD that connects to CRM Online (configured using these steps). It queries the Web API.

The application needs to run with no user interaction... but unfortunately the call to AcquireTokenSilentAsync always fails and only AcquireTokenAsync works. This makes a user login dialog appear which fails the user interaction requirement!

Is there any way to prevent this prompt, either by saving the login somewhere on the client machine (which hasn't worked so far) or perhaps using a certificate (but how do you do this?) or something else?

I'm using the ADAL for .NET v3.10.305110106 release. The following code is used to authenticate:

private static async Task PerformOnlineAuthentication()
{
    _authInfo = new AuthInfo();  // This is just a simple class of parameters

    Console.Write("URL (include /api/data/v8.x): ");
    var url = Console.ReadLine();

    BaseUri = new Uri(url);
    var absoluteUri = BaseUri.AbsoluteUri;
    _authInfo.Resource = absoluteUri;

    Console.Write("ClientId: ");
    var clientId = Console.ReadLine();
    _authInfo.ClientId = clientId;

    Console.Write("RedirectUri: ");
    var redirectUri = Console.ReadLine();
    _authInfo.RedirectUri = new Uri(redirectUri);

    var authResourceUrl = new Uri($"{_authInfo.Resource}/api/data/");
    var authenticationParameters = await AuthenticationParameters.CreateFromResourceUrlAsync(authResourceUrl);

    _authInfo.AuthorityUrl = authenticationParameters.Authority;
    _authInfo.Resource = authenticationParameters.Resource;

    _authInfo.Context = new AuthenticationContext(_authInfo.AuthorityUrl, false);
}

private static async Task RefreshAccessToken()
{
    if (!IsCrmOnline())
        return;

    Console.WriteLine($"Acquiring token from: {_authInfo.Resource}");
    AuthenticationResult authResult;
    try
    {
        authResult = await _authInfo.Context.AcquireTokenSilentAsync(_authInfo.Resource, _authInfo.ClientId);
    }
    catch (AdalSilentTokenAcquisitionException astae)
    {
        Console.WriteLine(astae.Message);
        authResult = await _authInfo.Context.AcquireTokenAsync(_authInfo.Resource, _authInfo.ClientId, _authInfo.RedirectUri, new PlatformParameters(PromptBehavior.RefreshSession));
    }

    HttpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
}
Alex Angas
  • 59,219
  • 41
  • 137
  • 210
  • hi, did you check this one https://github.com/Azure-Samples/active-directory-dotnet-native-headless ? it has one time user name - pwd entering only. – Aravind May 23 '16 at 07:16

2 Answers2

5

Thanks to @aravind who pointed out the active-directory-dotnet-native-headless sample.

The sample contains a FileCache class which inherits from Microsoft.IdentityModel.Clients.ActiveDirectory.TokenCache. That class manages caching of the credentials to an encrypted file on disk. This means that there is only one prompt on first run and after that the credentials are locally stored.

The final pieces of the puzzle are:

  1. Calling a different constructor signature to initialize AuthenticationContext with the FileCache:

    _authInfo.Context = new AuthenticationContext(
        _authInfo.AuthorityUrl, false, new FileCache());
    
  2. Obtaining credentials from the user into a Microsoft.IdentityModel.Clients.ActiveDirectory.UserPasswordCredential object (see the TextualPrompt() method in the sample)

  3. Passing the credentials to a different method signature for AcquireTokenAsync():

    authResult = await _authInfo.Context.AcquireTokenAsync(
        _authInfo.Resource, _authInfo.ClientId, userCredential);
    
Community
  • 1
  • 1
Alex Angas
  • 59,219
  • 41
  • 137
  • 210
1

If "application needs to run with no user interaction" use ClientCredential flow eg:

public static string GetAccessTokenUsingClientCredentialFlow(Credential cred) {           

        AuthenticationContext ac = new AuthenticationContext(cred.Authority);
        AuthenticationResult r = ac.AcquireTokenAsync(cred.ResourceId, new ClientCredential(cred.ClientId, cred.ClientSecret)).Result;
        return r.AccessToken;
    }
malfus
  • 381
  • 3
  • 8