Sample: Windows 8 desktop modern OData app

 

Applies To: Dynamics 365 (online), Dynamics 365 (on-premises), Dynamics CRM 2016, Dynamics CRM Online

This sample code is for Microsoft Dynamics CRM 2015 and Microsoft Dynamics CRM Online 2015 Update. This code can be found in the following location in the download package:

SampleCode\CS\ModernAndMobileApps\ModernOdataApp

Download the Microsoft Dynamics CRM SDK package.

Requirements

This sample requires the Microsoft.Preview.WindowsAzure.ActiveDirectory.Authentication and Microsoft.IdentityModel.Clients.ActiveDirectory NuGet packages. These packages are automatically downloaded and installed when you load the project’s solution.

For more information about the requirements for running the sample code provided in this SDK, see Use the sample and helper code.

Demonstrates

Windows 8 sample app main screen

Tiled user interface of the sample app

This sample shows how to write a Windows 8.1 desktop modern application that can send requests to the organization web service without linking to the SDK assemblies. This sample uses the Microsoft Azure Active Directory Authentication Library (ADAL) and the OData protocol.

While there are seven tiles displayed on the main app page, only the Accounts and Tasks tiles are connected to event handler code. The other tiles are just placeholders.

The example code is configured for use with the Microsoft Dynamics 365 (online) server and a fictitious organization, but can be used with an IFD server also.

Code snippets showing just the key sections of the full sample are shown later in this topic.

Example

The following code snippet shows how to authenticate the user with the organization web service.


using Microsoft.IdentityModel.Clients.ActiveDirectory;
using System;
using System.Threading.Tasks;
using Windows.UI.Popups;
using Windows.Security.Authentication.Web;
using System.Net;
using System.Threading;
using System.IO;
using System.Text;

namespace ModernOdataApp
{
    /// <summary>
    /// Manages authentication with the organization web service.
    /// </summary>
    public static class CurrentEnvironment
    {
        # region Class Level Members

        private static AuthenticationContext _authenticationContext;

        // TODO Set these string values as approppriate for your app registration and organization.
        // For more information, see the SDK topic "Walkthrough: Register an app with Active Directory".
        private const string _clientID = "893262be-fbdc-4556-9325-9f863b69495b";
        public const string CrmServiceUrl = "https://my-domain.crm.dynamics.com/";

        # endregion

        // <summary>
        /// Perform any required app initialization.
        /// This is where authentication with Active Directory is performed.
        public static async Task<string> Initialize()
        {
            Uri serviceUrl = new System.Uri(CrmServiceUrl + "/XRMServices/2011/Organization.svc/web?SdkClientVersion=6.1.0000.0000");

            // Dyamics CRM Online OAuth URL.
            string _oauthUrl = DiscoveryAuthority(serviceUrl);

            // Obtain the redirect URL for the app. This is only needed for app registration.
            Uri redirectUri = WebAuthenticationBroker.GetCurrentApplicationCallbackUri();

            // Obtain an authentication token to access the web service. 
            _authenticationContext = new AuthenticationContext(_oauthUrl, false);
            AuthenticationResult result = await _authenticationContext.AcquireTokenAsync(CrmServiceUrl, _clientID, redirectUri);

            // Verify that an access token was successfully acquired.
            if (result.Status != AuthenticationStatus.Success)
            {
                if (result.Error == "authentication_failed")
                {
                    // Try again.
                    _authenticationContext = new AuthenticationContext(_oauthUrl, false);
                    result = await _authenticationContext.AcquireTokenAsync(CrmServiceUrl, _clientID, redirectUri);
                }
                else
                {
                    DisplayErrorWhenAcquireTokenFails(result);
                }
            }
            return result.AccessToken;
        }

        /// <summary>
        /// Discover the authentication authority.
        /// </summary>
        /// <param name="serviceUrl">The URL of the organization's SOAP endpoint. </param>
        /// <returns>The authority URL.</returns>
        /// <remarks>The service URL must contain the SdkClient property.</remarks>
        /// <example>https://contoso.crm.dynamics.com/XRMServices/2011/Organization.svc/web?SdkClientVersion=6.1.0.533;</example>
        public static string DiscoveryAuthority(Uri serviceUrl)
        {
            // Use AuthenticationParameters to send a request to the organization's endpoint and
            // receive tenant information in the 401 challenge. 
            Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationParameters parameters = null;
            HttpWebResponse response = null;
            try
            {
                // Create a web request where the authorization header contains the word "Bearer".
                HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(serviceUrl);

                // The response is to be encoded.
                httpWebRequest.ContentType = "application/x-www-form-urlencoded";
                response = (HttpWebResponse)httpWebRequest.GetResponse();
            }

            catch (WebException ex)
            {
                response = (HttpWebResponse)ex.Response;

                // A 401 error should be returned. Extract any parameters from the response.
                // The response should contain an authorization_uri parameter.
                parameters = Microsoft.IdentityModel.Clients.ActiveDirectory.AuthenticationParameters.
                    CreateFromResponseAuthenticateHeader((response.Headers)["WWW-Authenticate"]);
            }
            finally
            {
                if (response != null)
                    response.Dispose();
            }
            // Return the authority URL.
            return parameters.Authority;
        }

        /// <summary>
        /// Returns a response from an Internet resource. 
        /// </summary>       
        public static WebResponse GetResponse(this WebRequest request)
        {
            AutoResetEvent autoResetEvent = new AutoResetEvent(false);
            IAsyncResult asyncResult = request.BeginGetResponse(r => autoResetEvent.Set(), null);

            // Wait until the call is finished
            autoResetEvent.WaitOne(DefaultRequestTimeout);
            return request.EndGetResponse(asyncResult);
        }

        /// <summary>
        /// Get the DefaultRequestTimeout from the server.
        /// </summary>
        public static TimeSpan DefaultRequestTimeout { get; set; }

        /// <summary>
        /// Display an error message to the user.
        /// </summary>
        /// <param name="result">The authentication result returned from AcquireTokenAsync().</param>
        private static async void DisplayErrorWhenAcquireTokenFails(AuthenticationResult result)
        {
            MessageDialog dialog;

            switch (result.Error)
            {
                case "authentication_canceled":
                    // User cancelled, so no need to display a message.
                    break;
                case "temporarily_unavailable":
                case "server_error":
                    dialog = new MessageDialog("Please retry the operation. If the error continues, please contact your administrator.",
                        "Sorry, an error has occurred.");
                    await dialog.ShowAsync();
                    break;
                default:
                    // An error occurred when acquiring a token so show the error description in a MessageDialog.
                    dialog = new MessageDialog(string.Format(
                        "If the error continues, please contact your administrator.\n\nError: {0}\n\nError Description:\n\n{1}",
                        result.Error, result.ErrorDescription), "Sorry, an error has occurred.");
                    await dialog.ShowAsync();
                    break;
            }
        }
    }
}

For this code to work, you must first register your app with a supported identity provider (AD FS or Microsoft Azure Active Directory). Next, you must set the variable values for _clientID and CrmServiceUrl in the code. The value for client ID was defined during app registration. More information:  Walkthrough: Register a Dynamics 365 app with Active Directory

Example

The following code snippet shows how to retrieve entity records from the organization web service using OData protocol code in an HTTP request. The authentication access token is placed in the authorization header.


using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

namespace ModernOdataApp
{
    public static class HttpRequestBuilder
    {
        /// <summary>
        /// Retrieve entity record data from the organization web service. 
        /// </summary>
        /// <param name="accessToken">The web service authentication access token.</param>
        /// <param name="Columns">The entity attributes to retrieve.</param>
        /// <param name="entity">The target entity for which the data should be retreived.</param>
        /// <returns>Response from the web service.</returns>
        /// <remarks>Builds an OData HTTP request using passed parameters and sends the request to the server.</remarks>
        public static async Task<string> Retrieve(string accessToken, string[] Columns, string entity)
        {
            // Build a list of entity attributes to retrieve as a string.
            string columnsSet = "";
            foreach (string Column in Columns)
            {
                columnsSet += "," + Column;
            }

            // The URL for the OData organization web service.
            string url = CurrentEnvironment.CrmServiceUrl + "/XRMServices/2011/OrganizationData.svc/" + entity + "?$select=" + columnsSet.Remove(0, 1) + "";

            // Build and send the HTTP request.
            HttpClient httpClient = new HttpClient();
            httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
            HttpRequestMessage req = new HttpRequestMessage(HttpMethod.Get, url);
            req.Method = HttpMethod.Get;

            // Wait for the web service response.
            HttpResponseMessage response;
            response = await httpClient.SendAsync(req);
            var responseBodyAsText = await response.Content.ReadAsStringAsync();

            return responseBodyAsText;
        }
    }
}

See Also

Write mobile and modern apps
Azure Authentication Library (AAL) for Windows Store: a Deep Dive
Securing a Windows Store Application and REST Web Service Using Azure AD
OData.org

Microsoft Dynamics 365

© 2016 Microsoft. All rights reserved. Copyright