Proxy de proveedor de identidades

En este documento se explica cómo crear un proxy para interactuar con proveedores de identidades personalizados o avanzados que usan el protocolo OAuth2.

Bot Framework permite a los usuarios iniciar sesión con varios proveedores de identidades que usan el protocolo OAuth2. Sin embargo, los proveedores de identidades pueden desviarse del protocolo OAuth2 principal, ofreciendo funcionalidades más avanzadas o opciones de inicio de sesión alternativas. En estos casos, es posible que no encuentre una configuración de configuración de conexión adecuada que funcione para usted. Una posible solución es hacer lo siguiente:

  1. Escriba un proxy de proveedor de OAuth2 que esté entre el servicio de token de Bot Framework y el proveedor de identidades más personalizado o avanzado .
  2. Configure la configuración de conexión para llamar a este proxy y haga que este proxy realice las llamadas al proveedor de identidades personalizado o avanzado. El proxy también puede asignar o transformar respuestas para que se ajusten a lo que espera el servicio de token de Bot Framework.

Servicio de proxy de OAuth2

Para compilar un servicio de proxy de OAuth2, debe implementar un servicio REST con dos API de OAuth2: una para la autorización y otra para recuperar un token. A continuación, encontrará un ejemplo de C# de cada uno de estos métodos y lo que puede hacer en estos métodos para llamar a un proveedor de identidades personalizado o avanzado.

Autorización de la API

La API de autorización es un HTTP GET que autoriza al autor de la llamada, genera una propiedad de código y redirige al URI de redireccionamiento.

[HttpGet("authorize")]
public ActionResult Authorize(
    string response_type, 
    string client_id, 
    string state, 
    string redirect_uri, 
    string scope = null)
{
    // validate parameters
    if (string.IsNullOrEmpty(state))
    {
        return BadRequest("Authorize request missing parameter 'state'");
    }

    if (string.IsNullOrEmpty(redirect_uri))
    {
        return BadRequest("Authorize request missing parameter 'redirect_uri'");
    }

    // redirect to an external identity provider, 
    // or for this sample, generate a code and token pair and redirect to the redirect_uri

    var code = Guid.NewGuid().ToString("n");
    var token = Guid.NewGuid().ToString("n");
    _tokens.AddOrUpdate(code, token, (c, t) => token);

    return Redirect($"{redirect_uri}?code={code}&state={state}");
}

API de token

La API de token es una solicitud HTTP POST a la que llama el servicio de token de Bot Framework. El servicio de token de Bot Framework enviará y client_idclient_secret en el cuerpo de la solicitud. Estos valores deben validarse o pasarse al proveedor de identidades personalizado o avanzado. La respuesta a esta llamada es un objeto JSON que contiene el access_token valor de expiración y del token (se omiten todos los demás valores). Si el proveedor de identidades devuelve un id_token valor u otro valor que desea devolver en su lugar, solo tiene que asignarlo a la propiedad de la access_token respuesta antes de devolverlo.

[HttpPost("token")]
public async Task<ActionResult> Token()
{
    string body;

    using (var reader = new StreamReader(Request.Body))
    {
        body = await reader.ReadToEndAsync();
    }

    if (string.IsNullOrEmpty(body))
    {
        return BadRequest("Token request missing body");
    }

    var parameters = HttpUtility.ParseQueryString(body);
    string authorizationCode = parameters["code"];
    string grantType = parameters["grant_type"];
    string clientId = parameters["client_id"];
    string clientSecret = parameters["client_secret"];
    string redirectUri= parameters["redirect_uri"];

    // Validate any of these parameters here, or call out to an external identity provider with them

    if (_tokens.TryRemove(authorizationCode, out string token))
    {
        return Ok(new TokenResponse()
        {
            AccessToken = token,
            ExpiresIn = 3600,
            TokenType = "custom",
        });
    }
    else
    {
        return BadRequest("Token request body did not contain parameter 'code'");
    }
}

Configuración de configuración de conexión de proxy

Una vez que se ejecute el servicio de proxy de OAuth2, puede crear una configuración de conexión del proveedor de servicios de OAuth en el recurso de Azure AI Bot Service. Siga los pasos descritos a continuación.

  1. Asigne un nombre a la configuración de conexión.
  2. Seleccione el proveedor de servicios genérico de Oauth 2 .
  3. Escriba un identificador de cliente y un secreto de cliente para la conexión. Estos valores pueden ser proporcionados por el proveedor de identidades avanzado o personalizado, o podrían ser específicos solo para el proxy si el proveedor de identidades que está usando no usa el identificador de cliente y el secreto.
  4. Para la dirección URL de autorización, debe copiar la dirección de la API REST de autorización, por ejemplo https://proxy.com/api/oauth/authorize.
  5. Para la dirección URL de token y actualización, debe copiar la dirección de la API rest del token, por ejemplo https://proxy.com/api/oauth/token. La dirección URL de Intercambio de tokens solo es válida para proveedores basados en AAD, por lo que se puede omitir.
  6. Por último, agregue todos los ámbitos que sean adecuados.

OAuthController para ASP.NET aplicación web

using Microsoft.AspNetCore.Mvc;
using Newtonsoft.Json;
using System;
using System.Collections.Concurrent;
using System.IO;
using System.Threading.Tasks;
using System.Web;

namespace CustomOAuthProvider.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class OAuthController : ControllerBase
    {
        ConcurrentDictionary<string, string> _tokens;

        public OAuthController(ConcurrentDictionary<string, string> tokens)
        {
            _tokens = tokens;
        }

        [HttpGet("authorize")]
        public ActionResult Authorize(
            string response_type, 
            string client_id, 
            string state, 
            string redirect_uri, 
            string scope = null)
        {
            if (string.IsNullOrEmpty(state))
            {
                return BadRequest("Authorize request missing parameter 'state'");
            }

            if (string.IsNullOrEmpty(redirect_uri))
            {
                return BadRequest("Authorize request missing parameter 'redirect_uri'");
            }

            // reidrect to an external identity provider, 
            // or for this sample, generte a code and token pair and redirect to the redirect_uri

            var code = Guid.NewGuid().ToString("n");
            var token = Guid.NewGuid().ToString("n");
            _tokens.AddOrUpdate(code, token, (c, t) => token);

            return Redirect($"{redirect_uri}?code={code}&state={state}");
        }

        [HttpPost("token")]
        public async Task<ActionResult> Token()
        {
            string body;

            using (var reader = new StreamReader(Request.Body))
            {
                body = await reader.ReadToEndAsync();
            }

            if (string.IsNullOrEmpty(body))
            {
                return BadRequest("Token request missing body");
            }

            var parameters = HttpUtility.ParseQueryString(body);
            string authorizationCode = parameters["code"];
            string grantType = parameters["grant_type"];
            string clientId = parameters["client_id"];
            string clientSecret = parameters["client_secret"];
            string redirectUri= parameters["redirect_uri"];

            // Validate any of these parameters here, or call out to an external identity provider with them

            if (_tokens.TryRemove(authorizationCode, out string token))
            {
                return Ok(new TokenResponse()
                {
                    AccessToken = token,
                    ExpiresIn = 3600,
                    TokenType = "custom",
                });
            }
            else
            {
                return BadRequest("Token request body did not contain parameter 'code'");
            }
        }
    }

    public class TokenResponse
    {
        [JsonProperty("access_token")]
        public string AccessToken { get; set; }

        [JsonProperty("id_token")]
        public string IdToken { get; set; }

        [JsonProperty("token_type")]
        public string TokenType { get; set; }

        [JsonProperty("expires_in")]
        public int ExpiresIn { get; set; }

        [JsonProperty("refresh_token")]
        public string RefreshToken { get; set; }

        [JsonProperty("scope")]
        public string Scope { get; set; }
    }
}