Compartir a través de


Tutorial: Autenticación multiempresa entre servidores

 

Publicado: enero de 2017

Se aplica a: Dynamics 365 (online)

En este tutorial se describirán los pasos para crear una aplicación web multiempresa que se pueda conectar a un inquilino de Actualización de diciembre de 2016 para Microsoft Dynamics 365 (online) mediante la plantilla de la aplicación web MVC de Microsoft Visual Studio 2015.

Requisitos

  • Visual Studio 2015 con herramientas de desarrollo web instaladas

  • Un inquilino de Actualización de diciembre de 2016 para Microsoft Dynamics 365 (online) asociado al inquilino de Azure Active Directory (Azure AD).

  • Un segundo inquilino de Actualización de diciembre de 2016 para Microsoft Dynamics 365 (online) asociado a otro inquilino de Azure AD. Este inquilino representa un suscriptor de la aplicación. Puede ser una suscripción de prueba a Actualización de diciembre de 2016 para Microsoft Dynamics 365 (online).

Objetivo de este tutorial

Al completar este tutorial tendrá una aplicación web MVC que usará la WhoAmIRequest Class para recuperar datos sobre el usuario que la aplicación usa para conectarse al inquilino de Dynamics 365 (online).

Al ejecutar la aplicación correctamente verá un comando Iniciar sesión en la esquina superior derecha.

The sign in command in the app

Haga clic en el comando Iniciar sesión y pasará a Azure AD para sus credenciales.

Una vez que haya iniciado sesión, verá que hay un comando WhoAmI.

The WhoAmI command

Haga clic en WhoAmI, y verá lo siguiente:

Results of a WhoAmI request

Cuando busca el inquilino de Dynamics 365 verá que los resultados devueltos del mensaje de WhoAmI hacen referencia a una cuenta de usuario de la aplicación específica que ha configurado para que la use la aplicación web en lugar de la cuenta de usuario que usa actualmente.

Comprobar el inquilino de Azure AD

Antes de comenzar, conéctese al Centro de administración de Office 365https://portal.office.com y en el desplegable Centros de administración, compruebe que ve Dynamics 365 y Azure AD.

Admin Centers with Azure Active Directory and Dynamics 365

Si su suscripción de Azure AD no está asociada a una suscripción de Dynamics 365, no podrá conceder privilegios para que su aplicación acceda a los datos de Dynamics 365.

Si no ve esta opción, vea Registre su suscripción gratuita a Azure Active Directory para obtener información sobre cómo registrarse para obtener su suscripción de Azure AD.

Si ya tiene una suscripción de Azure pero no está asociada a su cuenta de Microsoft Office 365, vea Asociar su cuenta de Office 365 con Azure AD para crear y administrar aplicaciones.

Crear una aplicación web MCV

Con Visual Studio 2015, puede crear una nueva aplicación web MVC y registrarla con el inquilino de Azure AD.

  1. Abra Visual Studio 2015.

  2. Asegúrese de que Cuenta de Microsoft en el que ha iniciado sesión es el mismo con acceso al inquilino de Azure AD que desea usar para registrar su aplicación.

  3. Haga clic en Nuevo proyecto y seleccione .NET Framework 4.6.1 y la plantilla Aplicación web de ASP.NET.

    Haga clic en Aceptar, y en el diálogo Nuevo proyecto de ASP.NET seleccione MVC.

  4. Haga clic en el botón Cambiar autenticación y, en el cuadro de diálogo seleccione Cuentas de trabajo y centro educativo.

  5. En la lista desplegable, seleccione Nube – Varias organizaciones.

    ASP.NET MVC Change Authentication Dialog

  6. Haga clic en Aceptar y complete la inicialización del proyecto.

    Nota

    Al crear un proyecto de Visual Studio de esta manera se registrará la aplicación con el inquilino de Azure AD y se agregarán las siguientes claves a Web.Config appSettings:

    <add key="ida:ClientId" value="baee6b74-3c39-4c04-bfa5-4414f3dd1c26" />
    <add key="ida:AADInstance" value="https://login.microsoftonline.com/" />
    <add key="ida:ClientSecret" value="HyPjzuRCbIl/7VUJ2+vG/+Gia6t1+5y4dvtKAcyztL4=" /> 
    

Registre su aplicación en Azure AD

Si ha realizado los pasos en Crear una aplicación web MCV, debe ver que el proyecto de aplicación web que creó en Visual Studio está registrado ya en sus aplicaciones de Azure AD. Pero si hay un paso más que debe realizar en el portal de Azure AD.

  1. Vaya a https://portal.azure.com y seleccione Azure Active Directory.

  2. Haga clic en Registros de la aplicación y busque la aplicación que ha creado mediante Visual Studio. En el área General, compruebe las propiedades:

    Application registration data in Azure Active Directory

  3. Compruebe que coincida la propiedad Identificador de aplicación con el valor de ClientId añadido en su Web.Config appSettings.

  4. El valor de Dirección URL de la página principal debe coincidir con la propiedad SSL URL en el proyecto de Visual Studio y debe dirigir a una dirección URL localhost, es decir https://localhost:44392/.

    Nota

    Necesitará cambiar esto posteriormente cuando publique realmente la aplicación. Pero debe configurar esto con el valor localhost correcto para depuración.

  5. Debe proporcionar los privilegios de la aplicación para tener acceso a los datos de Dynamics 365. En el área Acceso de API haga clic en Permisos requeridos. Debe ver que ya tiene permisos para Windows Azure Active Directory.

  6. Haga clic en Agregar y, a continuación, en Seleccionar una API. En la lista, seleccione Dynamics 365 y luego haga clic en el botón Seleccionar.

  7. En Seleccionar permisos, seleccione Acceso a Dynamics 365 como usuarios de la organización. A continuación, haga clic en el botón Seleccionar.

  8. Haga clic en Hecho para agregar estos permisos. Cuando finalice deberá ver los permisos aplicados:

    Dynamics 365 permissions applied to application in Azure Active Directory

  9. En el área Acceso de API, confirme que se haya agregado un valor Clave. El valor Clave no está visible en el portal de Azure vez que se haya creado la aplicación, pero este valor se agregó a la Web.Config appSettings como ClientSecret.

Crear un usuario de la aplicación

Siguiendo los pasos de Cree manualmente un usuario de la aplicación de Dynamics 365, cree un usuario de la aplicación con el valor Id. de la aplicación del registro de la aplicación que también es el mismo que el valor de ClientId en la Web.Config.

Agregar ensamblados

Agregue los siguientes paquetes de NuGet a su proyecto

Paquete

Versión

Microsoft.CrmSdk.CoreAssemblies

Versión más reciente

Microsoft.IdentityModel.Clients.ActiveDirectory

2.22.302111727

Microsoft.IdentityModel.Tokens

5.0.0

Microsoft.Azure.ActiveDirectory.GraphClient

2.1.0

Nota

No actualice los ensamblados de Microsoft.IdentityModel.Clients.ActiveDirectory a la versión más reciente. La versión 3.x de estos ensamblados cambió una interfaz de la que depende Microsoft.CrmSdk.CoreAssemblies.

Para obtener información sobre la administración de paquetes de NuGet, consulte Documentación de NuGet: Administrar paquetes de NuGet mediante la interfaz de usuario

Aplicar cambios de código a la plantilla MVC

Los siguientes cambios de código proporcionarán funcionalidad básica para usar el mensaje de Dynamics 365WhoAmI y comprobar que la identidad de la cuenta de usuario de la aplicación está siendo usada por la aplicación.

Web.config

Agregue las siguientes claves a la appSettings.

<add key="ida:OrganizationHostName" value="https://{0}.crm.dynamics.com" /> 

La cadena ida:OrganizationHostName tendrá el nombre de la organización en línea de Dynamics 365 del suscriptor agregado en el marcador para que se acceda al servicio correcto.

<add key="owin:appStartup" value="<your app namespace>.Startup" />

La cadena owin:appStartup garantiza que el software intermedio OWIN use la clase Startup en este proyecto. En caso contrario se mostrará el error siguiente:

- No assembly found containing an OwinStartupAttribute.
- No assembly found containing a Startup or [AssemblyName].Startup class.

Más información: ASP.NET: Detección de clase de inicio OWIN

Controllers/HomeController.cs

Agregue el decorador AllowAnonymous a la acción Index. Esto permite el acceso a la página predeterminada sin autenticación.

using System.Web.Mvc;

namespace SampleApp.Controllers
{
    [Authorize]
    public class HomeController : Controller
    {
        [AllowAnonymous]
        public ActionResult Index()
        {
            return View();
        }

        public ActionResult About()
        {
            ViewBag.Message = "Your application description page.";

            return View();
        }

        public ActionResult Contact()
        {
            ViewBag.Message = "Your contact page.";

            return View();
        }
    }
}

Nota

En la aplicación web o servicio, no se espera que permita acceso anónimo. El acceso anónimo se usa aquí para simplificar. El control del acceso a la aplicación se encuentra fuera del ámbito de este tutorial.

Views/Shared/_Layout.cshtml

Para mostrar el vínculo de comando WhoAmI para usuarios autenticados, deberá editar este archivo.

Sitúe el elemento div con la clase navbar-collapse collapse y edítelo para incluir el código siguiente:

<div class="navbar-collapse collapse">
    <ul class="nav navbar-nav">
     <li>@Html.ActionLink("Home", "Index", "Home")</li>
     <li>@Html.ActionLink("About", "About", "Home")</li>
     <li>@Html.ActionLink("Contact", "Contact", "Home")</li>
     @if (Request.IsAuthenticated)
     {
         <li>@Html.ActionLink("WhoAmI", "Index", "CrmSdk")</li>
     }
    </ul>

    @Html.Partial("_LoginPartial")
   </div>

App_Start/Startup.Auth.cs

Los siguientes cambios invocarán el marco de trabajo de consentimiento cuando un nuevo inquilino inicie sesión en la aplicación:

public partial class Startup
 {
  private static string clientId = ConfigurationManager.AppSettings["ida:ClientId"];
  private string appKey = ConfigurationManager.AppSettings["ida:ClientSecret"];
  //Not used   
  //private string graphResourceID = "https://graph.windows.net";    
  private static string aadInstance = ConfigurationManager.AppSettings["ida:AADInstance"];
  private string authority = aadInstance + "common";
  private ApplicationDbContext db = new ApplicationDbContext();

  //Added
  private string OrganizationHostName = ConfigurationManager.AppSettings["ida:OrganizationHostName"];

  public void ConfigureAuth(IAppBuilder app)
  {

   app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);

   app.UseCookieAuthentication(new CookieAuthenticationOptions { });

   app.UseOpenIdConnectAuthentication(
       new OpenIdConnectAuthenticationOptions
       {
        ClientId = clientId,
        Authority = authority,
        TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters
        {
         /*
         instead of using the default validation 
         (validating against a single issuer value, as we do in line of business apps), 
         we inject our own multitenant validation logic
         */
         ValidateIssuer = false,
        },
        Notifications = new OpenIdConnectAuthenticationNotifications()
        {
         SecurityTokenValidated = (context) =>
                  {
                   return Task.FromResult(0);
                  },
         AuthorizationCodeReceived = (context) =>
                  {
                   var code = context.Code;

                   ClientCredential credential = new ClientCredential(clientId, appKey);
                   string tenantID = context
                    .AuthenticationTicket
                    .Identity
                    .FindFirst("https://schemas.microsoft.com/identity/claims/tenantid")
                    .Value;

                   /* Not used
                  string signedInUserID = context
                     .AuthenticationTicket
                     .Identity
                     .FindFirst(ClaimTypes.NameIdentifier)
                     .Value;  
                     */

                   //Added
                   var resource = string.Format(OrganizationHostName, '*');
                   //Added
                   Uri returnUri = new Uri(
                    HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)
                    );

                   /* Changed below
                    AuthenticationContext authContext = 
                    new AuthenticationContext(
                     aadInstance + tenantID, 
                     new ADALTokenCache(signedInUserID)
                     );
                    */
                   //Changed version
                   AuthenticationContext authContext =
                   new AuthenticationContext(aadInstance + tenantID);

                   /* Changed below
                   AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
                       code, 
                       new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)), 
                       credential, 
                       graphResourceID);
                   */
                   //Changed version
                   AuthenticationResult result = authContext.AcquireTokenByAuthorizationCode(
                       code,
                       new Uri(HttpContext.Current.Request.Url.GetLeftPart(UriPartial.Path)),
                       credential,
                       resource);

                   return Task.FromResult(0);
                  },
         AuthenticationFailed = (context) =>
                  {
                   context.OwinContext.Response.Redirect("/Home/Error");
                   context.HandleResponse(); // Suppress the exception
                   return Task.FromResult(0);
                  }
        }
       });

  }
 }

Agregue Controllers/CrmSdkController

Agregue el siguiente CrmSdkController.cs a la carpeta Controllers. Este código ejecutará el mensaje WhoAmI

  1. Haga clic con el botón secundario en la carpeta Controllers y seleccione Agregar > Controlador…

  2. En el diálogo Agregar andamio, seleccione Controlador MVC5 – Vacío

  3. Haga clic en Agregar

  4. Pegue el siguiente código en sustitución de <Your app namespace> con el espacio de nombres de su aplicación.

using Microsoft.IdentityModel.Clients.ActiveDirectory; 
using Microsoft.Xrm.Sdk; 
using Microsoft.Xrm.Sdk.WebServiceClient; 
using System; using System.Configuration; 
using System.Linq; 
using System.Security.Claims; 
using System.Web.Mvc;

namespace <Your app namespace>
{
 [Authorize]
 public class CrmSdkController : Controller
    {

  private string clientId = 
   ConfigurationManager.AppSettings["ida:ClientId"];
  private string authority = 
   ConfigurationManager.AppSettings["ida:AADInstance"] + "common";
  private string aadInstance = 
   ConfigurationManager.AppSettings["ida:AADInstance"];
  private string OrganizationHostName = 
   ConfigurationManager.AppSettings["ida:OrganizationHostName"];
  private string appKey = 
   ConfigurationManager.AppSettings["ida:ClientSecret"];


  // GET: CrmSdk
  public ActionResult Index()
  {
   string tenantID = ClaimsPrincipal
    .Current
    .FindFirst("https://schemas.microsoft.com/identity/claims/tenantid")
    .Value;
   // Clean organization name from user logged
   string organizationName = User.Identity.Name.Substring(
    User.Identity.Name.IndexOf('@') + 1, 
    User.Identity.Name.IndexOf('.') - (User.Identity.Name.IndexOf('@') + 1)
    );
   //string crmResourceId = "https://[orgname].crm.microsoftonline.com";
   var resource = string.Format(OrganizationHostName, organizationName);
   // Request a token using application credentials
   ClientCredential clientcred = new ClientCredential(clientId, appKey);
   AuthenticationContext authenticationContext = 
    new AuthenticationContext(aadInstance + tenantID);
   AuthenticationResult authenticationResult = 
    authenticationContext.AcquireToken(resource, clientcred);
   var requestedToken = authenticationResult.AccessToken;
   // Invoke SDK using using the requested token
   using (var sdkService =
    new OrganizationWebProxyClient(
     GetServiceUrl(organizationName), false)
     )
   {
    sdkService.HeaderToken = requestedToken;
    OrganizationRequest request = new OrganizationRequest() {
     RequestName = "WhoAmI"
    };
    OrganizationResponse response = sdkService.Execute(request);
    return View((object)string.Join(",", response.Results.ToList()));
   }
  }

  private Uri GetServiceUrl(string organizationName)
  {
   var organizationUrl = new Uri(
    string.Format(OrganizationHostName, organizationName)
    );
   return new Uri(
    organizationUrl + 
    @"/xrmservices/2011/organization.svc/web?SdkClientVersion=8.2"
);
  }
 }
}

Views/CrmSdk

Agregue una nueva vista con el nombre Index.

  1. Haga clic con el botón secundario en la carpeta CrmSdk y seleccione Agregar > Ver…

  2. En el diálogo Agregar vista, establezca los siguientes valores:

    MVC Add View Dialog

  3. Haga clic en Agregar

  4. Reemplace el código generado con el siguiente:

    @model string
    @{
     ViewBag.Title = "SDK Connect";
    }
    
    
    <h2>@ViewBag.Title.</h2>
    
    <p>Connected and executed sdk command WhoAmI.</p>
    
    <p>Value: @Model</p>
    

Depure la aplicación

Cuando presione F5 para depurar la aplicación puede obtener el error de que no se confía en el certificado que está accediendo a localhost mediante SSL. A continuación hay algunos vínculos para resolver este problema con Visual Studio e IIS Express:

Nota

Para este paso, puede usar simplemente el Cuenta de Microsoft asociado con el inquilino de Azure AD y el inquilino de Dynamics 365 con el que esté asociado. Esto demuestra realmente un escenario multiempresa. Lo haremos en el paso siguiente. Este paso sólo es para comprobar que el código funciona antes de introducir la complejidad adicional de comprobar la funcionalidad real multiempresa.

Consulte los pasos descritos en Objetivo de este tutorial para probar la aplicación.

En este punto puede comprobar que se usó la cuenta de usuario de la aplicación. Una forma simple de comprobarlo es usar la API web de Dynamics 365. Escriba la dirección URL siguiente en una pestaña o ventana separada, sustituyendo el valor de UserId de la aplicación.

[Organization URI]/api/data/v8.2/systemusers(<UserId value>)?$select=fullname

La respuesta de JSON deberá tener el siguiente aspecto. Tenga en cuenta que el valor de fullname será el del usuario de la aplicación que creó en el paso Crear un usuario de la aplicación, en lugar del usuario de Dynamics 365 que usó para iniciar sesión en la aplicación.

    {
        "@odata.context": "[Organization Uri]/api/data/v8.2/$metadata#systemusers(fullname)/$entity",
        "@odata.etag": "W/\"603849\"",
        "fullname": "S2S User",
        "systemuserid": "31914b34-be8d-e611-80d8-00155d892ddc",
        "ownerid": "31914b34-be8d-e611-80d8-00155d892ddc"
    }

Configurar suscriptor de prueba

Ahora que ha comprobado que funciona la aplicación, es hora de probar la conectividad con otro inquilino de Dynamics 365 (online). Utilizando otra organización de Dynamics 365 (online) deberá ejecutar los pasos siguientes.

Dar consentimiento desde el inquilino de suscripción

Para dar consentimiento, lleve a cabo los pasos siguientes mientras inicia sesión como administrador de Azure AD:

  1. Cuando está la depuración su aplicación, abra una ventana aparte de InPrivate o de incógnito.

  2. En el campo de dirección del tipo de ventana escriba la dirección URL de la aplicación, es decir https://localhost:44392/

  3. Haga clic en el botón Iniciar sesión y se le pedirá que conceda consentimiento.

    Azure Active Directory consent form

Tras conceder consentimiento volverá a la aplicación, pero aún no podrá usarla. Si hace clic en WhoAmI en este punto puede esperar la excepción siguiente:

System.ServiceModel.Security.MessageSecurityException
HResult=-2146233087
  Message=The HTTP request is unauthorized with client authentication scheme 'Anonymous'. The authentication header received from the server was 'Bearer authorization_uri=https://login.windows.net/4baaeaaf-2771-4583-99eb-7c7e39aa1e74/oauth2/authorize, resource_id=https://<org name>.crm.dynamics.com/'.
InnerException.Message =The remote server returned an error: (401) Unauthorized.

Concediendo consentimiento, la aplicación del inquilino de Azure AD se agregará a las aplicaciones en el inquilino del directorio activo del suscriptor.

Crear un rol de seguridad personalizado en el inquilino del suscriptor

El usuario de la aplicación que deberá crear debe estar asociado a un rol de seguridad personalizado que defina sus privilegios. Para este paso de prueba manual, primero debe crear manualmente un rol de seguridad personalizado.Más información:TechNet: Creación o edición de roles de seguridad

Nota

El usuario de la aplicación no puede estar asociado con uno de los roles de seguridad de Dynamics 365 predeterminados. Debe crear un rol de seguridad personalizado para asociar con el usuario de la aplicación.

Crear el usuario de la aplicación del suscriptor

Con el objetivo de este tutorial, crearemos manualmente el usuario de la aplicación para comprobar la conectividad desde otro inquilino. Al implementar para suscriptores reales, le convendrá automatizar esto.Más información:Preparar un método para implementar el usuario de la aplicación

Puede crear el usuario de la aplicación manualmente usando los mismos valores que usó para la organización de desarrollo en Crear un usuario de la aplicación. La excepción es que debe haber completado el paso para conceder consentimiento primero. Cuando guarda el usuario, se establecerán los valores de URI de Id. de aplicación y Id. de objeto de Azure AD. No podrá guardar el usuario si no ha concedido consentimiento primero.

Por último, asocie el usuario de la aplicación con el rol de seguridad personalizado que agregó en el paso anterior.

Pruebe la conexión del suscriptor

Repita los pasos descritos en Depure la aplicación, pero use las credenciales para un usuario del otro inquilino de Dynamics 365.

Ver también

Usar autenticación multiempresa entre servidores
Usar autenticación entre servidores de una sola empresa
Crear aplicaciones web mediante autenticación de servidor a servidor (S2S)
Conectarse a Microsoft Dynamics 365

Microsoft Dynamics 365

© 2017 Microsoft. Todos los derechos reservados. Copyright