Autenticación de Bots con Azure Active Directory
Tags: tutorial, authentication, azure, csharp
Published: 2019-12-27 00:50:09

Adicionar autenticación usando Azure AD es _'relativamente'_ sencillo.

_'Relativamente'_?, si relativamente porque aunque el concepto de como hacer una integración con el directorio activo es ámpliamente conocido, los detalles para hacer dicha integración no suelen ser tan obvios.

Para poder usar de manera intuitiva Azure AD sería necesario tener algunos conocimientos previos de los flujos de autenticación modernos (OAuth principalmente), y admitámoslo ese no es el caso la mayoría de las veces así que hay que darse unas cuentas vueltas a la documentación y hay que reconocer que muchas veces eso tampoco es suficiente.

En esta entrada de blog me enfocaré en como realizar integración de un bot creado con **Bot Framework** y por extensión hospedado en  **Azure Web App Bot** (o corriendo localmente).

> lo sé, suena redundante, pero me gusta decir: "mejor que so-sobre y no que fa-falte"

## Resumen

Hay un conjunto de pasos para realizar la integración y en algunos casos no importa el orden en que los realices por ejemplo podrías crear el Web App Bot primero, pero hay una razón por la cual es mucho mejor leer esta entrada de blog que seguir el pie de la letra la documentación. Si sigues estos pasos no habrá lugar a confusiones, en especial en escenarios corporativos cuando es posible que te encuentres con ambientes de Azure con múltiples suscripciones o AD tenants.

Si, ya he llorado lagrimas y no quiero que te pase lo mismo, así que aquí te presento una serie de pasos en los que tienes que seguir el orden propuesto para obtener los resultados deseados, rápidamente y sin confusiones.

Con el propósito de enfocarnos en la integración y no en el desarrollo del bot vamos a utilizar la librería de ejemplos del Bot Framework y con uno de ellos probaremos que la integración ha funcionado de manera exitosa.

* [Registrar el Bot en Azure AD](#registrar-el-bot-en-azure-ad)
* [Configuración de la App Registrada](#configuraci%c3%b3n-de-la-app-registrada)
* [Crear y configurar un Web App Bot en Azure](#crear-y-configurar-un-web-app-bot-en-azure)
* [Configuración de la solución en Visual Studio](#configuración-de-la-solución-en-visual-studio)
* [Configuración del emulador](#configuración-del-emulador)
* [Prueba de Autenticación con Azure AD](#prueba-de-autenticación-con-azure-ad)

## Registrar el Bot en Azure AD

### Registro inicial de la aplicación

1. En el [portal de Azure](https://portal.azure.com) vamos al Directorio Activo, puedes hacerlo como quieras, a mi en particular me gusta usar la barra de búsqueda así

    ![Barra de búsqueda](https://thepracticaldev.s3.amazonaws.com/i/3gb7bfcot9fv4vyey8u4.png)

1. Una vez en el Azure AD vamos a 'App Registrations' y damos click en 'New Registration'

    ![app registrations](https://thepracticaldev.s3.amazonaws.com/i/jy09n68x26ai85zbhwoj.png)

1. En la ventana emergente le ponemos nombre al bot y en "Supported account types" nos aseguramos de seleccionar la indicada, dado que los bots la mayoría de las veces son para atender incluso a gente fuera de nuestra organización se recomienda que usemos la 2 opción, si nuestro bot requiere autenticación de otras plataformas con Microsoft ID como cuentas de outlook o de Xbox podríamos usar la 3ra.

    ![Register application](https://thepracticaldev.s3.amazonaws.com/i/dkhi2hsdj67pzlckeh6v.png)

1. Un poco más abajo del paso anterior encontramos "Platform configuration" y está marcado como opcional. Puedes dejarlo en blanco si quieres, en mi caso lo marcaré con la primera opción ya que , por supuesto, el Bot es una parte aplicación web y otra cliente móvil.

    ![Platform configuration](https://thepracticaldev.s3.amazonaws.com/i/2c42nntwgldwg2a8bd1i.png)

1. Click en "Register" y esperamos unos segundos
1. Y listo! ... por ahora, porque antes otra explicación.

Ahora que tenemos registrada la aplicación necesitamos entender la mecánica básica del flujo de autenticación.

En corto:

* La aplicación se presenta ante en Azure AD, para ello demuestra su identidad con un **ID** (llamado Client ID) y un **Password** (llamado Secret)
* Si el directorio la conoce le devuelve un token que debe usar en cualquier transacción que requiera hacer con el AD
* Cuando un usuario inicia sesión desde la aplicación, la aplicación usa una URL provista por el Azure AD en la que el usuario puede ingresar sus datos de acceso de manera segura
* Al iniciar de manera exitosa el usuario es redirigido a una **URL de la aplicación** (Redirect URL) incluyendo en los datos del request (URL, Headers o a veces body) un token de acceso para el usuario y otros metadatos.

He resaltado tres componentes, al lado de cada uno una breve explicación

* **Client ID**: Es único por cada aplicación
* **Secret**: Pueden haber varios, desaparecen una vez generados, así que es mejor copiarlos, usarlos y olvidarse de ellos. Si se requiere de nuevo en ese caso mejor generar uno nuevo.
* **Redirect URL**: Pueden ser varias, son las URL ***autorizadas*** de la aplicación, donde está puede analizar los contenidos del request (redirect) que le hicieron y extraer el token del usuario y los metadatos que lo acompañan.

### Configuración de la App Registrada

1. En el Azure AD vamos a la App Registrada

    ![App Registrada](https://thepracticaldev.s3.amazonaws.com/i/2mykdwvtqeu8h5lpntol.png)

1. En Overview logramos identificar dos datos muy importantes, que para facilidad vale la pena que guardes para más adelante. El primero de ellos es el Client ID y el segundo Tenant ID que no es otra cosa que el ID del directorio Activo donde se registró la aplicación.

    ![Overview](https://thepracticaldev.s3.amazonaws.com/i/uglprnjz7horrz9c0zkc.png)

    Id|Value
    ---|---
    Client ID|`da2e26d8-a9c9-442a-8831-c972fe6f281a`
    Tenant ID|`bcc54888-4288-4bbd-c041-3225637c7154`

1. En el panel de la izquierda navegamos hasta "Authentication" y allí damos click en "Add a platform"

    ![Add a platform](https://thepracticaldev.s3.amazonaws.com/i/hhfzx9tmfqoiyhk2e8cx.png)

1. En el panel emergente damos click en el botón 🌐 Web
1. En Redirect URIs colocamos `https://token.botframework.com/.auth/web/redirect` todo lo demás lo dejamos tal como está. Damos click en "Configure"

    ![Redirect Uri](https://thepracticaldev.s3.amazonaws.com/i/en5kkgqqgp8jczcuc494.png)

1. A continuación navegamos a "Certificates & Secrets" en el sidebar y damos click en "New client secret"

    ![New client secret](https://thepracticaldev.s3.amazonaws.com/i/hcrl4nnujq52h06xp3ac.png)

1. En la ventana emergente ponle un nombre que te ayude a recordar a quien o para que usaste ese secreto de la aplicación, selecciona el tiempo de expiración que desees para el secreto, en este caso he seleccionado "Never" pero en cada compañía manejan políticas diferentes para ello. Click en "Add".

    ![Agregar secreto](https://thepracticaldev.s3.amazonaws.com/i/3d2u9er1620bhl1h8ggq.png)

1. Una vez creado **copia de inmediato el secreto**, una vez generado, cuando regreses a esta ventana, ya no lo podrás recuperar. Les muestro el mio porque igual solo existirá mientras creo este artículo.

Description|Expires|Value
---|---|---
juankbot|12/26/2020|`j32e5fbN@lk[Su0oZXmLFnazaPgt@uv_`

Con esto terminamos nuestra configuración en el Azure AD, los datos relevantes que debemos llevarnos son

Item|Value
---|---
Client ID|`da2e26d8-a9c9-442a-8831-c972fe6f281a`
Tenant ID| `bcc54888-4288-4bbd-c041-3225637c7154`
Secret|`j32e5fbN@lk[Su0oZXmLFnazaPgt@uv_`

### Permisos en el Microsoft Graph (opcional en este momento)

Ya tenemos nuestra aplicación registrada en el Azure AD y tenemos los datos para el acceso, pero a ¿Qué cosas le vamos a dar acceso?.

Podemos dar acceso a infinidad de cosas de Productos de Microsoft, del Microsoft Graph e incluso de aplicaciones de terceros o propias.

Hay 2 tipos de permisos, delegados y de aplicación.

Hablamos de "Permisos de aplicación" cuando la aplicación corre como un servicio en segundo plano sin que ningún usuario deba iniciar sesión.

Como nuestro bot necesita actuar en nombre de un usuario (usualmente para conectarse con aplicaciones o servicios a los cuales el usuario tiene acceso completo o limitado) necesitamos permisos delegados.

Por el momento solo nos interesa autenticarnos e iniciar sesión, así que debemos conceder los siguientes permisos cuando se acceda desde  nuestra aplicación.

* `profile`
* `openid`
* `offline_access`

Para hacerlo debemos seguir los siguiente pasos en el Azure AD.

1. Navegar hasta el registro de la aplicación, ingresar en el registro
1. Navegar en el sidebar hasta "API Permissions" y dar click en "Add a permission"

    ![Azure AD permisos de aplicación](https://thepracticaldev.s3.amazonaws.com/i/smx5w1p3e0e4ftlurrco.png)

1. En el panel emergente seleccionamos "Microsoft Graph"

    ![Microsoft Graph](https://thepracticaldev.s3.amazonaws.com/i/4346bxdy2nkzdojekgh5.png)

1. Seguidamente damos click en "Delegated permissions" y usando la barra de búsqueda, buscamos y seleccionamos `profile`,`openid`,`offline_access`, damos click en "Add permissions"

    ![Microsoft Graph](https://thepracticaldev.s3.amazonaws.com/i/tmehy2anwmvqrquazaso.png)

1. Una vez hecho podemos dejar 'pre-autorizados' estos permisos para todos los miembros de la organización, sino lo hacemos cuando cada usuario inicie sesión se le preguntará si desea conceder estos permisos a la aplicación.
1. Esperamos unos segundos y damos click en "Refresh" de tal manera que el botón "Grant admin consent for..." se active. Le damos click.
1. En el diálogo emergente iniciamos sesión, se nos preguntará si queremos dar consentimiento para estos permisos en toda la organización, aceptamos.

    ![Consentimiento del administrador](https://thepracticaldev.s3.amazonaws.com/i/iz984tvjmjnm6r4819wm.png)

Con esto finalizamos la configuración de permisos en el AD.

## Crear y configurar un Web App Bot en Azure

### Creación del Web App Bot en Azure

Ahora crearemos un Azure Web App Bot, que en escencia es el bot que conectaremos con Azure AD.

1. Creamos un nuevo grupo de recursos y para este caso lo llamaremos `juank-rg`, para hacerlo en el home de [Azure portal](https://portal.azure.com) le damos click en donde indica la siguiente figura.

    ![Nuevo recurso](https://thepracticaldev.s3.amazonaws.com/i/85fmf4zojb65onhaj9q9.png)

    Una vez allí buscamos "Resouce Group", colócale un nombre adecuado, selecciona la región de tu conveniencia y procede a crearlo.

1. Ve al grupo de recursos recién creado, dale click en "Add"

    ![Adicionar Recursos](https://thepracticaldev.s3.amazonaws.com/i/wvk33qe1f8chiron8igx.png)

1. En el panel de creación busca "Web App Bot" y procede a crearlo
1. Hasta este punto configura la información según consideres pertinente.

    ![configuración básica del servicio de Bot](https://thepracticaldev.s3.amazonaws.com/i/21qtmdd66sfbv3f0muhg.png)

1. Selecciona el App Service plan correspondiente, sino tienes un App Service Plan o no sabes qué es, procede a crear uno desde allí mismo. Asegúrate de usar un nombre y región apropiados.
1. Recomiendo siempre, SIEMPRE, dejar habilitado Application Insights el día que tengas que hacer solución de problemas lo agradecerás. También recuerda configurar una región a apropiada par ti.

    ![configuración app service plan](https://thepracticaldev.s3.amazonaws.com/i/aleuebtp16s488wr4fdt.png)

1. ‼ Hemos llegado al punto importante de la integración con el Azure Active Directory. Haz click en el recuadro justo abajo de la configuración anterior "Microsoft App ID and Password".

    ![Microsoft App ID and Password](https://thepracticaldev.s3.amazonaws.com/i/dotw21mgq9br8iwad6i7.png)

   > ⚠ Al momento de escribir este artículo es __*MUY MALA IDEA*__ usar la opción de auto-creación, porqué? porque si lo haces de esa manera tendrás que volver al directorio activo y buscar la aplicación que creará automáticamente. Y si de suerte el directorio activo que usas está en otra suscripción de Azure, te harás un enredo que bueno... por eso escribo este artículo.

1. Dale click en "Create New" y pega allí los datos que guardamos anteriormente. Y finaliza dando click en "OK".

    ![Microsoft App ID and Password](https://thepracticaldev.s3.amazonaws.com/i/479do6wiiiew0n3dwbq6.png)

1. Al finalizar dale click en "Create" y la plataforma procederá a crear el Bot y los demás assets necesarios.

### Configuración del Web App Bot

Ahora que hemos creado en el servicio en Azure debemos hacer un par de configuraciones.

1. En el bot services vamos a Settings y en el panel de la derecha hacemos scroll hasta la parte inferior buscando "OAuth Connection Settings" Y damos click allí.

    ![Bot Settings](https://thepracticaldev.s3.amazonaws.com/i/uj9ae586g02qwqj1scmo.png)

1. Le colocamos un nombre adecuado a la conexión, ciertamente un Bot podría iniciar sesión contra varios Directorios diferentes, así que aquí debemos configurar nuestro directorio activo. Hay dos formas de hacerlo, con Azure AD v1 y Azure AD v2. Ambas funcionan pero en condiciones normales siempre deberías seleccionar la última versión disponible, en este momento Azure AD v2. Así que seleccionamos ese en la lista de service providers. Inmediatamente aparecen los campos que debemos llenar, para los cuales ya tenemos información. En el campo "Scopes" colocamos los valores que listo a continuación, los cuales indican que necesitamos permiso para iniciar sesión y acceder a la info del usuario. Esta lista puede cambiar dependiendo de que otras cosas queremos acceder del [Microsoft Graph](https://developer.microsoft.com/en-us/graph/).

    Item|Value
    ---|---
    Client ID|`da2e26d8-a9c9-442a-8831-c972fe6f281a`
    Tenant ID| `bcc54888-4288-4bbd-c041-3225637c7154`
    Secret|`j32e5fbN@lk[Su0oZXmLFnazaPgt@uv_`
    Scopes|`openid profile`

    La lista de scopes es *case-sensitive* y debe estar separada por espacios en blanco.
1. Le damos click en "Save" y queda así

    ![Bot OAuth Settings](https://thepracticaldev.s3.amazonaws.com/i/xjdjyfh458zxwbjuzr17.png)

1. Inmediatamente volvemos a abrirla para dar click en "Test Connection" allí debes iniciar sesión, usa tus credenciales para acceder al portal de Azure. Sino has hecho el paso opcional de [asignación de permisos en el Azure AD](#permisos-en-el-microsoft-graph-opcional-en-este-momento) verás una pantalla como está y como  eres admin te dará la opción de consentimiento de estos permisos para todos los usuarios de la organización, si ya habías hecho el paso de los permisos esta pantalla no aparecerá.

    ![Auth Permissions](https://thepracticaldev.s3.amazonaws.com/i/1v45vsbfy3z4edcblqnt.png)

1. Al autenticarte aparecerá la siguiente pantalla de comprobación donde puedes ver y copiar el token generado. Sino sale o recibes un error deberás revisar la configuración anterior o incluso volver a revisar el registro de la Aplicación en el Azure AD. Guarda y cierra el dialogo.

    ![Token generado](https://thepracticaldev.s3.amazonaws.com/i/drmg3ydapfs1bnwx3hqr.png)

1. Ahora que tenemos la autenticación del servicio configurada y verificada vamos a "Configuration" en el Sidebar y allí encontraremos "Application Settings" y justo en la lista de settings
   * `MicrosoftAppId`
   * `MicrosoftAppPassword`
  
    Verificamos que los valores correspondan, pista: siempre corresponden.

    > ⚠ Ten presente que aunque el setting `MicrosoftAppId` es editable, cambiarlos por el ID de una nueva aplicación que tengas en el Azure AD no servirá de nada, tu aplicación quedará inútil ya que este valor debe corresponder siempre con el que aparece en "Settings" que como podrás ver es un campo ineditable.

    ![Bot Config Settings](https://thepracticaldev.s3.amazonaws.com/i/1f31o2oy3281o7y8co8n.png)

    ![Settings ineditables](https://thepracticaldev.s3.amazonaws.com/i/6202u3zykcwzj5565u0q.png)

Con esto terminamos la configuración del servicio del Bot. Sin embargo para que el tema siguiente funcione se requiere un paso adicional así que lo haremos de una vez.

1. En la configuración agregamos un nuevo setting llamado `ConnectionName` y le asignamos el mismo nombre que usamos en la configuración de los "OAuth Settings", en mi caso `azure-ad-juank`.

    ![Setting adicional](https://thepracticaldev.s3.amazonaws.com/i/sjch48mo1yjbx53z566c.png)

## Configuración de la solución en Visual Studio

1. Como lo mencionamos al inicio, con el ánimo de enfocarnos en la configuración y no en el código, haremos uso de uno de los [ejemplos del Bot Framework](https://github.com/Microsoft/BotBuilder-Samples). {% github microsoft/BotBuilder-Samples no-readme %}

1. Usaremos cualquiera de los relacionados con autenticación en el repositorio, las configuraciones que aquí realice aplican para cualquier framework y lenguaje con las variantes normales que correspondan a cada uno.

1. En ese caso  usaré un ejemplo de C# net Core que se encuentra en "samples/csharp_dotnetcore/", llamado ["18.bot-authentication"](https://github.com/microsoft/BotBuilder-Samples/tree/master/samples/csharp_dotnetcore/18.bot-authentication).

    Te recomiendo que clones el repositorio y que trabajes luego solo en este proyecto de ejemplo de tal forma que en cada compilación no tardes tanto como si tuvieras los más de 45 ejemplos cargados.

1. Abrimos el archivo de configuración de la solución, el cual en el caso de C# es `appsettings.json` allí encontraremos los mismos settings que dejamos configurados en el Web App Bot, para poder probar el bot en el entorno local debemos tener lo settings locales.

    ```json
    {
    "MicrosoftAppId": "da2e26d8-a9c9-442a-8831-c972fe6f281a",
    "MicrosoftAppPassword": "j32e5fbN@lk[Su0oZXmLFnazaPgt@uv_",
    "ConnectionName": "azure-ad-juank"
    }
    ```

Eso es todo , el ejemplo va a funcionar.
Si te preguntas cual es el propósito del setting `ConnectionName` les el siguiente párrafo.

---

### TL;DR

Para que el bot pueda mostrar una ventana de login al usuario se requiere que te comuniques con las API de Auth de Azure AD y con el Auth setting que configuraste en el servicio Web App Bot. Y llevar el control de todo el flujo de autenticación por tu cuenta.

Hacer todo este trabajo manualmente no es imposible, pero si dispendioso, así que existe forma de crear una [Adaptive Card](https://adaptivecards.io/) para autenticación por OAuth, pero si quieres solucionarlo de manera aún más fácil el SDK de bot Framework, en este caso el SDK para net core, incluye un `OAuthPrompt` que internamente -adivino- hace uso de un Adaptive Card y se encarga de toda la lógica del flujo de autenticación.

Pues bien para que el `OAuthPrompt` funcione requiere información relacionada con nuestro Azure AD y nuestra aplicación...

Claro! el `MicrosoftAppId` y `MicrosoftAppPassword` son dos de esos datos, pero y todo lo demás?
El tenant Id, los permisos requeridos, las URL de autenticación, las de token, las URL autorizadas en el Azure AD etc, ¿Dónde están?.

Bueno esos datos los hemos dejado (y otros son resueltos) por el Web App Bot en el Auth Setting (en mi caso `azure-ad-juank`).

Así que por ello en `ConnectionName` hemos dejado el valor del Auth Setting del Web App Bot.

Dentro de la solución encontramos donde se hace la configuración del AuthPrompt aquí. `Dialogs/MAinDialog.cs`

```csharp
public MainDialog(IConfiguration configuration, ILogger<MainDialog> logger)
    : base(nameof(MainDialog), configuration["ConnectionName"])
{
    Logger = logger;

    AddDialog(new OAuthPrompt(
        nameof(OAuthPrompt),
        new OAuthPromptSettings
        {
            ConnectionName = ConnectionName,
            Text = "Please Sign In",
            Title = "Sign In",
            Timeout = 300000, // User has 5 minutes to login (1000 * 60 * 5)
        }));
```

Puedes ver claramente como extrae el nombre de la configuración y luego lo pasa al `OAuthPromptSettings`.

Dónde se muestra el `OAuthPrompt`? Más abajo justo en este método.

```csharp
private async Task<DialogTurnResult> PromptStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
    return await stepContext.BeginDialogAsync(nameof(OAuthPrompt), null, cancellationToken);
}
```

---

## ngrok

Ngrok es un proxy que permite que el bot en tu emulador se comporte como si estuviera corriendo desde azure, permitiendo que si es necesario use todos los servicios de azure Bot disponibles en el backend.

⚠⚠⚠ Debes hacer esto ❗❗❗
No entraré en detalles de como descargarlo y configurarlo, pero los encuentras aquí en [detalles al respecto](https://blog.botframework.com/2017/10/19/debug-channel-locally-using-ngrok/) 


## Configuración del emulador

Si ejecutamos la aplicación recién configurada desde Visual Studio o desde el kit de herramientas del Bot Framework tenemos esto.

![Bot Service Running](https://thepracticaldev.s3.amazonaws.com/i/hrbm6kqkjl2qk6enrk3k.png)

El Bot ya está corriendo, ahora debemos conectarnos con [emulador](https://github.com/microsoft/BotFramework-Emulator/releases) y con [ngrok](https://ngrok.com/). 


1. En la configuración del emulador en este ícono parte inferior izquierda ⚙ , encuentras la siguientes opciones de autenticación, ambas son soportadas, sin embargo para efectos de la prueba te recomiendo marcar solo la primera.

    ![Bot Emulator Config](https://thepracticaldev.s3.amazonaws.com/i/wkmi9dcg3ef5e4pbj8dy.png)

1. Cuando se configure la conexión con el bot es ⚠ IMPORTANTE configurar el `MicrosoftAppId` y `MicrosoftAppPassword`

    ![Conexión con el bot desde emulador](https://thepracticaldev.s3.amazonaws.com/i/49qu88xnrdabotwte9zl.png)

## Prueba de Autenticación con Azure AD

1. Teniendo la instancia del bot corriendo desde Visual Studio, realizamos la conexión con el emulador. **Si recibes este mensaje** entonces todo va bien, sino entonces debes revisar nuevamente los `MicrosoftAppId` y `MicrosoftAppPassword` en 
   * la configuración de la conexión en el emulador
   * en la solución
   * en el Bot Web App
   * en el Azure AD

    ![Bot Service Message](https://thepracticaldev.s3.amazonaws.com/i/0k9iu25yk9tixoegy6za.png)

1. El bot está diseñado para pedir autenticación con cualquier texto que ingreses, con excepción de que ingreses `logout` `¯\_(ツ)_/¯` . Así que ingresa cualquier texto como prueba, en este caso ingresé 123. Veras que el bot despliega el `OAuthPromt` pidiendo login. Damos click al botón.

    ![Bot Service Login](https://thepracticaldev.s3.amazonaws.com/i/k36stpbc728c3cdlmn1j.png)

1. Al darle click al enlace el emulador pedirá confirmación. Acéptala.

    ![Bot Emulator prompt](https://thepracticaldev.s3.amazonaws.com/i/hnk333y4y2o2yr60syrw.png)

1. Una vez inicies sesión notarás que **no te pide autorizar permisos** ya que  lo hemos hecho en alguna de las opciones ofrecidas anteriormente. Si iniciaste sesión exitosamente recibirás estos mensajes, dile que sí para que te muestre el token de usuario.

    ![Login exitoso](https://thepracticaldev.s3.amazonaws.com/i/nufpnnsi39b2lqbkgob7.png)

    ![Token de usuario](https://thepracticaldev.s3.amazonaws.com/i/oq4jomiiu476mqvs1ugb.png)

---

Eso es todo! hemos conectado un Bot de Microsoft Bot Framework con Azure Active Directory para realizar la autenticación.