3

I am modifying an internal management application to connect to our online hosted Dynamics 2016 instance.

Following some online tutorials, I have been using an OrganizationServiceProxy out of Microsoft.Xrm.Sdk.Client from the SDK.

This seems to need a username and password to connect, which works fine, but I would like to connect in some way that doesn't require a particular user's account details. I don't think the OAuth examples I've seen are suitable, as there is no UI, and no actual person to show an OAuth request to.

public class DynamicsHelper
{
    private OrganizationServiceProxy service;

    public void Connect(string serviceUri, string username, string password)
    {
            var credentials = new ClientCredentials();
            credentials.UserName.UserName = username;
            credentials.UserName.Password = password;

            var organizationUri = new Uri(serviceUri);
            this.service = new OrganizationServiceProxy(organizationUri, null, credentials, null);
    }
}

Is there a way to connect with an application token or API key?

Cylindric
  • 5,858
  • 5
  • 46
  • 68
  • I've never seen connection code that doesn't require creds, but I would check out these two pages from the SDK: https://msdn.microsoft.com/en-us/library/gg334502.aspx and https://msdn.microsoft.com/en-us/library/mt595799.aspx – Polshgiant Mar 22 '16 at 17:15
  • The second one is not really relevant - as I said, this is not a web page or anything that can present an OAuth request to a user. I guess the worst case is we have to create a dummy user for the admin service, although that has a cost associated with it. – Cylindric Mar 22 '16 at 17:37
  • You will not be able to access anything in CRM without a user; furthermore, there should be at least one role associated to the user to be considered as an active one. When you create an application that connects to CRM that is going to work inside a single Active Directory then you will not be needed to specify a password inside the connection string because the application will be running on behalf of one of the users added to the CRM system. So, even here, you still need the user. Question: why don't you want to have him? – Alex Mar 23 '16 at 04:40
  • This is an automatic server service that runs in the background to integrate to Dynamics. Typically these sorts of integrations I've created in the past do not use a "real" user account, as that account can expire, the user can leave or change their password, etc. Usually there is a facility for a service account, or an API key, or some other non-human credentials. If I need to create a fully licensed user I can, it just seemed worth confirming, as it's not something I've had to do to for any other web service. – Cylindric Mar 23 '16 at 09:21
  • Were you able to find a solution to this? I'm running into the same issue and if you were able to figure it out and post the solution as the answer that would be great! – BateTech Jul 18 '16 at 10:51
  • No, I ended up just creating a dedicated user and storing the credentials in config. – Cylindric Jul 20 '16 at 20:43

3 Answers3

2

I've found that to do this successfully, you'll need to setup all of the following:

  1. Create an application registration in Azure AD:
    • grant it API permissions for Dynamics, specifically "Access Dynamics 365 as organization users"
    • give it a dummy web redirect URI such as http://localhost/auth
    • generate a client secret and save it for later
  2. Create a user account in Azure AD and give it permissions to Dynamics.
  3. Create an application user record in Dynamics with the same email as the non-interactive user account above.
  4. Authenticate your application using the user account you've created.

For step 4, you'll want to open an new incognito window, construct a url using the following pattern and login using your user account credentials in step 2:

https://login.microsoftonline.com/<your aad tenant id>/oauth2/authorize?client_id=<client id>&response_type=code&redirect_uri=<redirect uri from step 1>&response_mode=query&resource=https://<organization name>.<region>.dynamics.com&state=<random value>

When this is done, you should see that your Dynamics application user has an Application ID and Application ID URI.

Now with your ClientId and ClientSecret, along with a few other organization specific variables, you can authenticate with Azure Active Directory (AAD) to acquire an oauth token and construct an OrganizationWebProxyClient. I've never found a complete code example of doing this, but I have developed the following for my own purposes. Note that the token you acquire has an expiry of 1 hr.

internal class ExampleClientProvider
{
    // Relevant nuget packages:
    // <package id="Microsoft.CrmSdk.CoreAssemblies" version="9.0.2.9" targetFramework="net472" />
    // <package id="Microsoft.IdentityModel.Clients.ActiveDirectory" version="4.5.1" targetFramework="net461" />

    // Relevant imports:
    // using Microsoft.IdentityModel.Clients.ActiveDirectory;
    // using Microsoft.Crm.Sdk.Messages;
    // using Microsoft.Xrm.Sdk;
    // using Microsoft.Xrm.Sdk.Client;
    // using Microsoft.Xrm.Sdk.WebServiceClient;

    private const string TenantId = "<your aad tenant id>";                 // from your app registration overview "Directory (tenant) ID"
    private const string ClientId = "<your client id>";                     // from your app registration overview "Application (client) ID"
    private const string ClientSecret = "<your client secret>";             // secret generated in step 1
    private const string LoginUrl = "https://login.microsoftonline.com";    // aad login url
    private const string OrganizationName = "<your organization name>";     // check your dynamics login url, e.g. https://<organization>.<region>.dynamics.com
    private const string OrganizationRegion = "<your organization region>"; // might be crm for north america, check your dynamics login url    

    private string GetServiceUrl()
    {
        return $"{GetResourceUrl()}/XRMServices/2011/Organization.svc/web";
    }

    private string GetResourceUrl()
    {
        return $"https://{OrganizationName}.api.{OrganizationRegion}.dynamics.com";
    }

    private string GetAuthorityUrl()
    {
        return $"{LoginUrl}/{TenantId}";
    }

    public async Task<OrganizationWebProxyClient> CreateClient()
    {
        var context = new AuthenticationContext(GetAuthorityUrl(), false);
        var token = await context.AcquireTokenAsync(GetResourceUrl(), new ClientCredential(ClientId, ClientSecret));

        return new OrganizationWebProxyClient(new Uri(GetServiceUrl()), true)
        {
            HeaderToken = token.AccessToken,
            SdkClientVersion = "9.1"
        };
    }

    public async Task<OrganizationServiceContext> CreateContext()
    {
        var client = await CreateClient();
        return new OrganizationServiceContext(client);
    }

    public async Task TestApiCall()
    {
        var context = await CreateContext();

        // send a test request to verify authentication is working
        var response = (WhoAmIResponse) context.Execute(new WhoAmIRequest());
    }
}
Michael Petito
  • 12,891
  • 4
  • 40
  • 54
0

With Microsoft Dynamics CRM Online or internet facing deployments When you use the Web API for CRM Online or an on-premises Internet-facing deployment (IFD) you must use OAuth as described in Connect to Microsoft Dynamics CRM web services using OAuth.

Before you can use OAuth authentication to connect with the CRM web services, your application must first be registered with Microsoft Azure Active Directory. Azure Active Directory is used to verify that your application is permitted access to the business data stored in a CRM tenant.

 // TODO Substitute your correct CRM root service address, 
 string resource = "https://mydomain.crm.dynamics.com";

// TODO Substitute your app registration values that can be obtained after you
// register the app in Active Directory on the Microsoft Azure portal.
string clientId = "e5cf0024-a66a-4f16-85ce-99ba97a24bb2";
string redirectUrl = "http://localhost/SdkSample";


// Authenticate the registered application with Azure Active Directory.
AuthenticationContext authContext = 
    new AuthenticationContext("https://login.windows.net/common", false);
AuthenticationResult result = 
    authContext.AcquireToken(resource, clientId, new Uri(redirectUrl));

P.S: Concerning your method, it is a best practice to not to store the password as clear text, crypt it, or encrypt the configuration sections for maximum security.

See walkhrough here

Hope this helps :)

hdoghmen
  • 3,175
  • 4
  • 29
  • 33
0

If I understand your question correctly, you want to connect to Dynamics 2016 (Dynamics 365) through a Registerd Azure Application with ClientId and Secret, instead of Username and Password. If this is correct, yes this is possible with the OrganizationWebProxyClient . You can even use strongly types assemblies.

var organizationWebProxyClient = new OrganizationWebProxyClient(GetServiceUrl(), true);
organizationWebProxyClient.HeaderToken = authToken.AccessToken;

OrganizationRequest request = new OrganizationRequest()
{
    RequestName = "WhoAmI"
};

WhoAmIResponse response = organizationWebProxyClient.Execute(new WhoAmIRequest()) as WhoAmIResponse;
Console.WriteLine(response.UserId);

Contact contact = new Contact();
contact.EMailAddress1 = "jennie.whiten@mycompany.com";
contact.FirstName = "Jennie";
contact.LastName = "White";
contact.Id = Guid.NewGuid();

organizationWebProxyClient.Create(contact);

To get the AccessToken, please refer to the following post Connect to Dynamics CRM WebApi from Console Application.

Replace line 66 (full source code)

authToken = await authContext.AcquireTokenAsync(resourceUrl, clientId, new Uri(redirectUrl), new PlatformParameters(PromptBehavior.Never));

with

authToken = await authContext.AcquireTokenAsync( resourceUrl, new ClientCredential(clientId, secret));

You can also check the following Link Authenticate Azure Function App to connect to Dynamics 365 CRM online that describes how to secure your credentials using the Azure Key Vault.

tinu73
  • 19
  • 2
  • 11