Partilhar via


Proxy de fornecedores de identidade

Este documento explica como criar um proxy para interagir com fornecedores de identidade personalizados ou avançados que utilizam o protocolo OAuth2.

O Bot Framework permite que os utilizadores iniciem sessão com vários fornecedores de identidade que utilizam o protocolo OAuth2. No entanto, os fornecedores de identidade podem desviar-se do protocolo OAuth2 principal, oferecendo capacidades mais avançadas ou opções alternativas de início de sessão. Nestes casos, poderá não encontrar uma configuração de definição de ligação adequada que funcione para si. Uma solução possível é fazer o seguinte:

  1. Escreva um proxy de fornecedor OAuth2 que esteja entre o serviço de tokens do Bot Framework e o fornecedor de identidade mais personalizado ou avançado .
  2. Configure a definição de ligação para chamar este proxy e peça a este proxy que faça as chamadas para o fornecedor de identidade personalizado ou avançado. O proxy também pode mapear ou transformar respostas para que estejam em conformidade com o que o serviço de tokens do Bot Framework espera.

Serviço Proxy OAuth2

Para criar um Serviço Proxy OAuth2, tem de implementar um serviço REST com duas APIs OAuth2: uma para autorização e outra para obter um token. Abaixo, encontrará um exemplo C# de cada um destes métodos e o que pode fazer nestes métodos para chamar um fornecedor de identidade personalizado ou avançado.

Autorizar a API

A API de autorização é um HTTP GET que autoriza o autor da chamada, gera uma propriedade de código e redireciona para o URI de redirecionamento.

[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

A API de Token é um HTTP POST chamado pelo serviço de tokens do Bot Framework. O serviço de tokens do Bot Framework enviará o client_id e client_secret no corpo do pedido. Estes valores devem ser validados e/ou transmitidos ao fornecedor de identidades personalizado ou avançado. A resposta a esta chamada é um objeto JSON que contém o access_token valor de expiração e e do token (todos os outros valores são ignorados). Se o seu fornecedor de identidade devolver um id_token ou outro valor que pretenda devolver, basta mapeá-lo para a access_token propriedade da sua resposta antes de regressar.

[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'");
    }
}

Configuração da Definição de Ligação do Proxy

Assim que tiver o serviço Proxy OAuth2 em execução, pode criar uma Definição de Ligação do Fornecedor de Serviços OAuth no recurso de Bot Service de IA do Azure. Siga os passos descritos abaixo.

  1. Atribua um nome à definição de ligação.
  2. Selecione o fornecedor de serviços Generic Oauth 2 .
  3. Introduza um ID de cliente e um segredo do cliente para a ligação. Estes valores podem ser fornecidos pelo seu fornecedor de identidade avançado ou personalizado, ou estes podem ser específicos apenas para o seu proxy se o fornecedor de identidade que está a utilizar não utilizar o ID de cliente e o segredo.
  4. Para o URL de Autorização, deve copiar o endereço da API REST de autorização, por exemplo https://proxy.com/api/oauth/authorize.
  5. Para o URL de Token e Atualização, deve copiar o endereço da API REST do token, por exemplo https://proxy.com/api/oauth/token. O URL do Exchange de Tokens é válido apenas para fornecedores baseados no AAD, pelo que pode ser ignorado.
  6. Por fim, adicione os âmbitos adequados.

OAuthController para ASP.NET aplicação 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; }
    }
}