Partager via


Proxy des fournisseurs d’identité

Ce document explique comment créer un proxy pour interagir avec des fournisseurs d’identité personnalisés ou avancés qui utilisent le protocole OAuth2.

Bot Framework permet aux utilisateurs de se connecter à l’aide de différents fournisseurs d’identité qui utilisent le protocole OAuth2. Toutefois, les fournisseurs d’identité peuvent s’écarter du protocole OAuth2 principal, en offrant des fonctionnalités plus avancées ou d’autres options de connexion. Dans ce cas, vous ne trouverez peut-être pas une configuration de paramètre de connexion appropriée qui vous convient. Une solution possible consiste à effectuer les opérations suivantes :

  1. Écrivez un proxy de fournisseur OAuth2 qui se trouve entre le service de jeton Bot Framework et le fournisseur d’identité plus personnalisé ou avancé .
  2. Configurez le paramètre de connexion pour appeler ce proxy et faites en sorte que ce proxy effectue les appels au fournisseur d’identité personnalisé ou avancé. Le proxy peut également mapper ou transformer des réponses pour les rendre conformes à ce que le service de jeton Bot Framework attend.

OAuth2 Proxy Service

Pour créer un service proxy OAuth2, vous devez implémenter un service REST avec deux API OAuth2 : une pour l’autorisation et l’autre pour la récupération d’un jeton. Vous trouverez ci-dessous un exemple C# de chacune de ces méthodes et ce que vous pouvez faire dans ces méthodes pour appeler un fournisseur d’identité personnalisé ou avancé.

Autoriser l’API

L’API d’autorisation est un GET HTTP qui autorise l’appelant, génère une propriété de code et redirige vers l’URI de redirection.

[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 jeton

L’API token est un HTTP POST appelé par le service de jeton Bot Framework. Le service de jeton Bot Framework envoie le client_id et client_secret dans le corps de la requête. Ces valeurs doivent être validées et/ou transmises au fournisseur d’identité personnalisé ou avancé. La réponse à cet appel est un objet JSON contenant la access_token valeur d’expiration et du jeton (toutes les autres valeurs sont ignorées). Si votre fournisseur d’identité retourne une id_token ou une autre valeur que vous souhaitez retourner à la place, il vous suffit de la mapper à la access_token propriété de votre réponse avant de revenir.

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

Configuration du paramètre de connexion proxy

Une fois que votre service proxy OAuth2 est en cours d’exécution, vous pouvez créer un paramètre de connexion de fournisseur de services OAuth sur votre ressource Azure AI Bot Service. Suivez les étapes décrites ci-dessous.

  1. Donnez un nom au paramètre de connexion.
  2. Sélectionnez le fournisseur de services Oauth 2 générique .
  3. Entrez un ID client et une clé secrète client pour la connexion. Ces valeurs peuvent être fournies par votre fournisseur d’identité avancé ou personnalisé, ou elles peuvent être spécifiques uniquement à votre proxy si le fournisseur d’identité que vous utilisez n’utilise pas l’ID client et le secret.
  4. Pour l’URL d’autorisation, vous devez copier l’adresse de votre API REST d’autorisation, par exemple https://proxy.com/api/oauth/authorize.
  5. Pour le jeton et l’URL d’actualisation, vous devez copier l’adresse de l’API REST de votre jeton, par exemple https://proxy.com/api/oauth/token. L’URL d’échange de jetons est valide uniquement pour les fournisseurs basés sur AAD et peut donc être ignorée.
  6. Enfin, ajoutez toutes les étendues appropriées.

OAuthController pour ASP.NET application 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; }
    }
}