ID プロバイダーのプロキシ
このドキュメントでは、OAuth2 プロトコルを使用するカスタムまたは高度な ID プロバイダーと対話するプロキシを作成する方法について説明します。
Bot Framework を使用すると、ユーザーは OAuth2 プロトコルを使用するさまざまな ID プロバイダーを使用してログインできます。 ただし、ID プロバイダーは、より高度な機能や代替サインイン オプションを提供することで、コア OAuth2 プロトコルから逸脱する可能性があります。 このような場合は、適切な 接続設定の構成 が見つからない場合があります。 考えられる解決策は、次の操作を行うことです。
- Bot Framework トークン サービスと、よりカスタマイズされた、または高度な ID プロバイダーの間にある OAuth2 プロバイダー プロキシを作成します。
- このプロキシを呼び出すように接続設定を構成し、このプロキシがカスタム ID プロバイダーまたは高度な ID プロバイダーを呼び出すようにします。 プロキシは、応答をマップまたは変換して、Bot Framework トークン サービスが期待する内容に準拠させることもできます。
OAuth2 プロキシ サービス
OAuth2 プロキシ サービスを構築するには、承認用とトークン取得用の 2 つの OAuth2 API を使用して REST サービスを実装する必要があります。 以下に、これらの各メソッドの C# の例と、カスタムまたは高度な ID プロバイダーを呼び出すためにこれらのメソッドで実行できる操作を示します。
API を承認する
authorize API は、呼び出し元を承認し、コード プロパティを生成し、リダイレクト URI にリダイレクトする HTTP GET です。
[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 は、Bot Framework トークン サービスによって呼び出される HTTP POST です。 Bot Framework トークン サービスは、 と client_secret
をclient_id
要求の本文で送信します。 これらの値は、検証するか、カスタムまたは高度な ID プロバイダーに渡す必要があります。
この呼び出しに対する応答は、トークンの access_token
と 有効期限の値を含む JSON オブジェクトです (他のすべての値は無視されます)。 ID プロバイダーが、代わりに返す またはその他の値を返 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 プロキシ サービスを実行したら、Azure AI Bot Service リソースに OAuth サービス プロバイダー接続設定を作成できます。 以下で説明する手順に従います。
- 接続設定に名前を付けます。
- 汎用 Oauth 2 サービス プロバイダーを選択します。
- 接続の [クライアント ID] と [クライアント シークレット ] を入力します。 これらの値は、高度な ID プロバイダーまたはカスタム ID プロバイダーによって提供される場合があります。または、使用している ID プロバイダーがクライアント ID とシークレットを使用しない場合は、プロキシのみに固有である可能性があります。
-
承認 URL の場合は、承認 REST API のアドレス (例:
https://proxy.com/api/oauth/authorize
) をコピーする必要があります。 -
[トークンと更新 URL] には、トークン REST API のアドレス (例:
https://proxy.com/api/oauth/token
) をコピーする必要があります。 トークン交換 URL は AAD ベースのプロバイダーに対してのみ有効であるため、無視できます。 - 最後に、適切なスコープを追加します。
ASP.NET Web アプリの OAuthController
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; }
}
}