There doesn't seem to be many resources online or in books about this and each one I was able to find seems to be structured differently compared if we are doing interactive logins, defined creds, or using the web api/secret key even though the difference is tiny.
Note: this is just my first stab at authentication so there might be better ways to do some of this. These are just examples that I have found to work well.
There are three different ways to accomplish authenticating with the D365FO system
1. Interactive login
2. Define username/password
3. Using secret key
Because option 2 requires the user the to login interactively before we can use it the examples I am providing will not go over it in my C# app but the code is listed at the bottom of this post.
If you receive the error The user or administrator has not consented to use the application with ID # named #. Send an interactive authorization request for this user and resource
It is because the exact scenario I decided has occurred and you need to login in interactively to grant it permission.
Files:
C# D365Auth - Download - This project includes the following:
The project includes the odataclient.cs/tt which is why it is so big
D365FO Azure Authentication - Interactive & Defined Creds (Secret key), Setting up a class to define multiple environments
SOAP - Calling custom webservice - single input/single output, No input/multiple output, Multiple inputs/multiple outputs, Single input/update record/single output(before and after update results)
JSON - No input/multiple outputs, Single input/single output
Odata - Calls customer entity to get basic customer info, filter customer entity by defined customer account
X++ D365FO - Download - This project includes the following:
Webservice class: method that expects inputclass outputs class, method that expects a string and returns a string, method with no parms and returns an output class, method that inspects 2 parms and returns a string.
It is good to note that I found if you name the service + service group with the same name you may run into issues even though it will compile.
Outside of downloading the project I thought I would post the 2 main methods I have defined for interactive and secret key login so it is easy to tell the difference.
Interactive:
public static AuthenticationResult GenerateAzureToken(AXEnvironments _environmentName)
{
//create the context of the token
AuthenticationContext context = new AuthenticationContext(_environmentName.AzureADTenant, TokenCache.DefaultShared);
//create token for the client
Task<AuthenticationResult> task = context.AcquireTokenAsync(_environmentName.AzureResource, _environmentName.NativeAzureClientAppId, new Uri(_environmentName.UriString), new PlatformParameters(PromptBehavior.Always));
//wait for the task to finsh
task.Wait();
//return the result
return task.Result;
}
Defined:
public static AuthenticationResult GenerateAzureToken(AXEnvironments _environment)
{
//create the credential object based upon the encrypted secret key in azure
ClientCredential creds = new ClientCredential(_environment.WebAzureClientAppId, _environment.AzureClientSecret);
//setup the context of the token
AuthenticationContext context = new AuthenticationContext(_environment.AzureADTenant, TokenCache.DefaultShared);
//generate the token
Task<AuthenticationResult> task = context.AcquireTokenAsync(_environment.AzureResource, creds);
//wait for the task to finish
task.Wait();
//return the result
return task.Result;
}
Both of the methods are called via
public static string GetAzureAuthenticationHeader(AXEnvironments _environment)
{
AuthenticationResult result;
string authHeader = "";
try
{
//generate the token and get an authorization
result = GenerateAzureToken(_environment);
authHeader = result.CreateAuthorizationHeader();
}
catch
{
authHeader = "";
}
return authHeader;
}
Which is passed to
string authHeader = D365Auth.AXAuthorizationDefined.GetAzureAuthenticationHeader(currentEviornment);
which is sent to the http request header.
Just for reference if you do want to use a defined username and password you can use the following
public static AuthenticationResult GenerateAzureToken(AXEnvironments _environment)
{
//create the credential object based upon the stored username/password
UserPasswordCredential credential = new UserPasswordCredential(_environment.UserName, _environment.Password);
//setup the context of the token
AuthenticationContext context = new AuthenticationContext(_environment.AzureADTenant, TokenCache.DefaultShared);
//generate the token
Task<AuthenticationResult> task = context.AcquireTokenAsync(_environment.AzureResource, _environment.AzureClientAppId, credential);
//wait for the task to finish
task.Wait();
//return the result
return task.Result;
}
This post assumes you have already setup authenication in Azure and D365FO which is gone over @ D365FO - Azure authentication setup for web services (defined creds and interactive login)