Прокси-сервер для поставщиков удостоверений
В этом документе объясняется, как создать прокси-сервер для взаимодействия с пользовательскими или расширенными поставщиками удостоверений, которые используют протокол OAuth2.
Bot Framework позволяет пользователям выполнять вход с помощью различных поставщиков удостоверений, использующих протокол OAuth2. Однако поставщики удостоверений могут отличаться от основного протокола OAuth2, предлагая более сложные возможности или альтернативные варианты входа. В таких случаях может не найти подходящую конфигурацию параметров подключения , которая подходит вам. Возможное решение — сделать следующее:
- Создайте прокси-сервер поставщика OAuth2 , который находится между службой маркеров Bot Framework и более настраиваемым или расширенным поставщиком удостоверений.
- Настройте параметр подключения для вызова этого прокси-сервера и предоставьте этому прокси-серверу выполнять вызовы к пользовательскому или расширенному поставщику удостоверений. Прокси-сервер также может сопоставлять или преобразовывать ответы, чтобы они соответствовали ожиданиям службы токенов Bot Framework.
Прокси-служба OAuth2
Чтобы создать прокси-службу OAuth2, необходимо реализовать службу REST с двумя API OAuth2: один для авторизации и один для получения маркера. Ниже приведен пример каждого из этих методов на C#, а также сведения о том, что можно сделать в этих методах для вызова пользовательского или расширенного поставщика удостоверений.
API авторизации
API авторизации — это HTTP GET , который авторизует вызывающий объект, создает свойство кода и перенаправляет на URI перенаправления.
[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 маркеров
API токена — это HTTP POST , вызываемый службой токенов Bot Framework. Служба токенов Bot Framework отправит client_id
и client_secret
в тексте запроса. Эти значения должны быть проверены и (или) переданы пользовательскому или расширенному поставщику удостоверений.
Ответом на этот вызов является объект JSON, access_token
содержащий значение и срок действия маркера (все остальные значения игнорируются). Если поставщик удостоверений возвращает id_token
или другое значение, которое вы хотите вернуть, необходимо просто сопоставить его со свойством access_token
ответа перед возвратом.
[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'");
}
}
Конфигурация параметров подключения прокси-сервера
После запуска прокси-службы OAuth2 вы можете создать параметр подключения поставщика служб OAuth в ресурсе azure AI Служба Bot. Выполните описанные ниже действия.
- Присвойте параметру подключения имя.
- Выберите поставщика услуг Generic OAuth 2 .
- Введите идентификатор клиента и секрет клиента для подключения. Эти значения могут быть предоставлены расширенным или настраиваемым поставщиком удостоверений или только для вашего прокси-сервера, если используемый поставщик удостоверений не использует идентификатор клиента и секрет.
- В качестве URL-адреса авторизации следует скопировать адрес REST API авторизации, например
https://proxy.com/api/oauth/authorize
. - В поле Маркер и URL-адрес обновления необходимо скопировать адрес REST API маркера, например
https://proxy.com/api/oauth/token
. URL-адрес обмена маркерами действителен только для поставщиков на основе AAD, поэтому его можно игнорировать. - Наконец, добавьте все подходящие области.
OAuthController для веб-приложения ASP.NET
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; }
}
}