Direct Line API 3.0 での認証
クライアントは、Direct Line API 3.0 に対する要求を、Bot Framework Portal の Direct Line チャネル構成ページから取得するシークレットを使用するか、ランタイム時に取得するトークンを使用して認証できます。 シークレットまたはトークンは、次の書式で各要求の Authorization
ヘッダー内に指定する必要があります。
Authorization: Bearer SECRET_OR_TOKEN
シークレットとトークン
Direct Line シークレットは、関連付けられているボットに属するすべての会話にアクセスするために使用できるマスター キーです。 シークレットは、トークンを取得するために使用することもできます。 シークレットには有効期限切れがありません。
Direct Line トークンは、1 つの会話にアクセスするために使用できるキーです。 トークンには有効期限がありますが、更新できます。
シークレット キーまたはトークンを使用するタイミングや、どちらを使用するかの決定は、セキュリティに関する考慮事項に基づいて行う必要があります。 シークレット キーの公開は、計画的に注意して行えば、許容される場合があります。 実際には、これが既定の動作です。これにより、クライアントが正当であるかどうかを Direct Line で判断できるためです。 ただし、一般に、ユーザー データを保持しようとしている場合は、セキュリティが問題になります。 詳細については、「セキュリティに関する考慮事項」セクションを参照してください。
サービス間アプリケーションを作成する場合は、Direct Line API 要求の Authorization
ヘッダー内にシークレットを指定することが最も簡単な方法です。 クライアントが Web ブラウザーまたはモバイル アプリで実行されるアプリケーションを記述する場合は、シークレットをトークン (1 つの会話でのみ機能し、更新されない限り有効期限が切れます) と交換し、そのトークンを、Direct Line API 要求の Authorization
ヘッダー内に指定できます。 自分にとって最適なセキュリティ モデルを選択してください。
Note
Direct Line クライアント資格情報は、ボットの資格情報とは異なります。 これにより、キーを個別に変更でき、ボットのパスワードを公開せずにクライアント トークンを共有できます。
Direct Line シークレットを取得する
Azure portal で、ボット用の Direct Line チャンネル構成ページを使用して、Direct Line シークレットを取得できます。
Direct Line トークンを生成する
1 つの会話にアクセスするために使用できる Direct Line トークンを生成するには、まず Azure portal の Direct Line チャンネル構成ページから Direct Line シークレットを取得します。 その後、次の要求を発行して、Direct Line シークレットを Direct Line トークンと交換します。
POST https://directline.botframework.com/v3/directline/tokens/generate
Authorization: Bearer SECRET
この要求の Authorization
ヘッダーの SECRET を、Direct Line シークレットの値に置き換えます。
以下のスニペットは、トークン生成要求と応答の例を示しています。
要求
POST https://directline.botframework.com/v3/directline/tokens/generate
Authorization: Bearer RCurR_XV9ZA.cwA.BKA.iaJrC8xpy8qbOF5xnR2vtCX7CZj0LdjAPGfiCpg4Fv0
要求ペイロードにはトークン パラメーターが含まれています。これは省略可能ですが、使用することをお勧めします。 Direct Line サービスに送り返すことができるトークンを生成するときに、次のペイロードを提供して、接続セキュリティを強化します。 これらの値を含めることによって、Direct Line によるユーザー ID と名前の追加セキュリティ検証が実行可能になり、悪意のあるクライアントによるこれらの値の改ざんが禁止されます。 これらの値を含めて、Direct Line の "会話更新" アクティビティ送信機能を向上させることもできます。これにより、ユーザーが会話に参加したときに直ちに会話更新を生成できるようになります。 この情報が指定されていない場合、ユーザーは、Direct Line が会話の更新情報を送信する前に、コンテンツを送信する必要があります。
{
"user": {
"id": "string",
"name": "string"
},
"trustedOrigins": [
"string"
]
}
パラメーター | Type | 説明 |
---|---|---|
user.id |
string | 省略可能。 トークン内でエンコードするためのチャネル固有のユーザー ID。 Direct Line ユーザーの場合、dl_ で始まる必要があります。 会話ごとに一意のユーザー ID を作成できます。セキュリティを強化するために、この ID は推測できないものにします。 |
user.name |
string | 省略可能。 トークン内でエンコードするためのユーザーの表示用フレンドリ名。 |
trustedOrigins |
文字列配列 | 省略可能。 トークン内に埋め込む信頼されたドメインの一覧。 これらはボットの Web チャット クライアントをホストできるドメインです。 これはご自身のボット用の Direct Line 構成ページの一覧と一致する必要があります。 |
回答
要求が成功した場合、応答には、1 つの会話で有効な token
と、このトークンの有効期限が切れるまでの秒数を示す expires_in
値が含まれます。 トークンを使い続けるには、期限切れになる前に、トークンを更新する 必要があります。
HTTP/1.1 200 OK
[other headers]
{
"conversationId": "abc123",
"token": "RCurR_XV9ZA.cwA.BKA.iaJrC8xpy8qbOF5xnR2vtCX7CZj0LdjAPGfiCpg4Fv0y8qbOF5xPGfiCpg4Fv0y8qqbOF5x8qbOF5xn",
"expires_in": 1800
}
トークンの生成と会話の開始
トークンの生成操作 (POST /v3/directline/tokens/generate
) と会話の開始操作 (POST /v3/directline/conversations
) は、どちらの操作も、1 つの会話にアクセスするために使用できる token
を返すという点で類似しています。 ただし、会話の開始操作とは異なり、トークンの生成操作では、会話は開始されず、ボットへの接触も行われず、WebSocket のストリーミング URL も作成されません。
トークンをクライアントに配布し、クライアントに会話を開始してほしい場合は、トークンの生成操作を使用します。 会話をすぐに開始するつもりの場合は、会話の開始操作を使用します。
Direct Line トークンを更新する
Direct Line トークンは、有効期限が切れていない限り、無制限に更新できます。 期限切れのトークンは更新できません。 Direct Line トークンを更新するには、次の要求を発行します。
POST https://directline.botframework.com/v3/directline/tokens/refresh
Authorization: Bearer TOKEN_TO_BE_REFRESHED
この要求の Authorization
ヘッダーの TOKEN_TO_BE_REFRESHED を、更新する Direct Line トークンに置き換えます。
以下のスニペットは、トークン更新要求と応答の例を示しています。
要求
POST https://directline.botframework.com/v3/directline/tokens/refresh
Authorization: Bearer CurR_XV9ZA.cwA.BKA.iaJrC8xpy8qbOF5xnR2vtCX7CZj0LdjAPGfiCpg4Fv0y8qbOF5xPGfiCpg4Fv0y8qqbOF5x8qbOF5xn
回答
要求が成功した場合、応答には、前のトークンと同じ会話で有効な、新しい token
と、新しいトークンの有効期限が切れるまでの秒数を示す expires_in
値が含まれます。 新しいトークンを有効のままにするには、有効期限が切れる前にトークンを更新する必要があります。
HTTP/1.1 200 OK
[other headers]
{
"conversationId": "abc123",
"token": "RCurR_XV9ZA.cwA.BKA.y8qbOF5xPGfiCpg4Fv0y8qqbOF5x8qbOF5xniaJrC8xpy8qbOF5xnR2vtCX7CZj0LdjAPGfiCpg4Fv0",
"expires_in": 1800
}
Azure AI Bot Service 認証
このセクションに記載されている情報は、「Azure AI Bot Service を介してボットに認証を追加する」の記事に基づいています。
Azure AI Bot Service 認証を使用すると、各種 ID プロバイダー (Microsoft Entra ID、GitHub、Uber など) に対してユーザーを認証し、これらからアクセス トークンを取得できます。 カスタム OAuth2 ID プロバイダーの認証も構成できます。 これだけで、サポートされているすべての ID プロバイダーとチャネルで動作する 1 つの認証コードを作成できます。 これらの新しい機能を使用するには:
- ボットで、ID プロバイダーでのアプリケーション登録の詳細を含む
settings
を静的に構成します。 - 前の手順で指定したアプリケーション情報に基づき、
OAuthCard
を使用して、ユーザーをサインインします。 - Azure AI Bot Service API を使用してアクセス トークンを取得します。
セキュリティに関する考慮事項
Web チャットで Azure AI Bot Service 認証を使用する場合、注意する必要がある重要なセキュリティの考慮事項がいくつかあります。
偽装。 偽装とは、攻撃者がボットに対して、攻撃者が別人であると信じ込ませる行為です。 Web チャットでは、攻撃者が Web チャット インスタンスのユーザー ID を変えて、他の誰かになりすます可能性があります。 この偽装を防ぐため、ボット開発者はユーザー ID を推測できないものにすることをおすすめします。
強化された認証オプションを有効にすると、Azure AI Bot Service であらゆるユーザー ID への変更を検出して、拒否することができます。 この場合、Direct Line からボットへのメッセージのユーザー ID (
Activity.From.Id
) は、Web チャットを初期化したときに使用したものと必ず同じになります。 この機能では、ユーザー ID が必ずdl_
で始まる必要があります。Note
トークンのシークレットを交換するときに User.Id が指定されている場合、その User.Id はトークンに埋め込まれます。 Direct Line では、ボットに送信されるメッセージに、アクティビティの From.Id としてその ID が含まれていることを確認します。クライアントが別 の From.Id を持つメッセージを Direct Line に送信すると、メッセージをボットに転送する 前にトークン の ID に変更されます。 このため、チャンネル シークレットがユーザー ID で初期化された後に、別のユーザー ID を使用することはできません。
ユーザー ID。 各ユーザーには、複数のユーザー ID があります。
- チャンネルのユーザー ID。
- ボットがやり取りする ID プロバイダーのユーザー ID。
ボットで、チャネルのユーザー A に ID プロバイダー P にサインインするよう要求する場合、サインイン プロセスでは、P にサインインするのはユーザー A であることを保証する必要があります。別のユーザー B がログインを許可される場合、ユーザー A は、ボットを通じてユーザー B のリソースにアクセスできるようになります。 Web チャットでは、次に説明するように、確実に適切なユーザーがサインインするために 2 つのメカニズムが用意されています。
以前は、ログインの最後に、ランダムに生成された 6 桁のコード (マジック コード) がユーザーに表示されました。 ユーザーは、サインイン プロセスを完了するために、サインインを開始したやり取りにおいて、このコードを入力する必要があります。 このメカニズムでは、ユーザー エクスペリエンスが悪化する傾向があります。 また、フィッシング攻撃を受けやすくなります。 悪意のあるユーザーが、別のユーザーになりすましてサインインし、フィッシング詐欺を通じてマジック コードを入手できます。
前の方法で問題が発生したため、Azure AI Bot Service では、マジック コードが不要になりました。 Azure AI Bot Service では、サインイン プロセスは必ず Web チャット自体と同じブラウザー セッションでのみ完了します。 ボット開発者がこの保護を有効にするには、ボットの Web チャット クライアントを信頼される側のドメインの一覧を含む Direct Line トークンを使用して、Web チャットを開始する必要があります。 以前は、ドキュメントに記載されていないオプション パラメーターを Direct Line トークン API に渡すことでのみ、このトークンを取得できました。 現在は、強化された認証オプションを使用して、Direct Line 構成ページで信頼されたドメイン (origin) の一覧を静的に指定できます。
詳細については、「Azure AI Bot Service を介してボットに認証を追加する」を参照してください。
コード例
次の .NET コントローラーは、強化された認証オプションが有効な状態で動作し、Direct Line トークンとユーザー ID を返します。
public class HomeController : Controller
{
public async Task<ActionResult> Index()
{
var secret = GetSecret();
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(
HttpMethod.Post,
$"https://directline.botframework.com/v3/directline/tokens/generate");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", secret);
var userId = $"dl_{Guid.NewGuid()}";
request.Content = new StringContent(
JsonConvert.SerializeObject(
new { User = new { Id = userId } }),
Encoding.UTF8,
"application/json");
var response = await client.SendAsync(request);
string token = String.Empty;
if (response.IsSuccessStatusCode)
{
var body = await response.Content.ReadAsStringAsync();
token = JsonConvert.DeserializeObject<DirectLineToken>(body).token;
}
var config = new ChatConfig()
{
Token = token,
UserId = userId
};
return View(config);
}
}
public class DirectLineToken
{
public string conversationId { get; set; }
public string token { get; set; }
public int expires_in { get; set; }
}
public class ChatConfig
{
public string Token { get; set; }
public string UserId { get; set; }
}
次の JavaScript コントローラーは、強化された認証オプションが有効な状態で動作し、Direct Line トークンとユーザー ID を返します。
var router = express.Router(); // get an instance of the express Router
// Get a directline configuration (accessed at GET /api/config)
const userId = "dl_" + createUniqueId();
router.get('/config', function(req, res) {
const options = {
method: 'POST',
uri: 'https://directline.botframework.com/v3/directline/tokens/generate',
headers: {
'Authorization': 'Bearer ' + secret
},
json: {
User: { Id: userId }
}
};
request.post(options, (error, response, body) => {
if (!error && response.statusCode < 300) {
res.json({
token: body.token,
userId: userId
});
}
else {
res.status(500).send('Call to retrieve token from Direct Line failed');
}
});
});