Direct Line API 3.0 中的身份验证
客户端可以使用从 Bot Framework 门户中的 Direct Line 通道配置页获取的机密或使用在运行时获得的令牌来对 Direct Line API 3.0 的请求进行身份验证。 应使用以下格式在每个请求的 Authorization
标头中指定机密或令牌:
Authorization: Bearer SECRET_OR_TOKEN
机密和令牌
Direct Line 机密是可用于访问属于关联的机器人的任何会话的主密钥。 机密还可用于获取令牌。 机密不会过期。
Direct Line 令牌是可用于访问单个会话的密钥。 令牌会过期,但可以进行刷新。
必须根据安全注意事项确定何时或者是否使用机密密钥或令牌。 在保持谨慎的前提下,可以有目的性地公开机密密钥。 事实上这是默认的行为,因为这样可以让 Direct Line 判断客户端是否合法。 不过,一般而言,如果你要尝试保存用户数据,则需要考虑到安全性。 有关详细信息,请参阅安全注意事项部分。
如果你要创建服务到服务应用程序,在 Direct Line API 请求的 Authorization
标头中指定机密可能是最简单的方法。 如果你要编写客户端在 Web 浏览器或移动应用中运行的应用程序,你可能想要交换你的机密以获取令牌(仅适用于单个会话,且在不刷新的情况下会过期)并在 Direct Line API 请求的 Authorization
标头中指定令牌。 选择最适合你的安全模型。
注意
你的 Direct Line 客户端凭据与你的机器人的凭据是不同的。 这让你能够独立修改你的密钥,并使你能够在不泄漏你的机器人的密码的情况下共享客户端令牌。
获取 Direct Line 机密
可以在 Azure 门户中通过 Direct Line 通道配置页为机器人获取 Direct Line 机密:
生成 Direct Line 令牌
若要生成可用于访问单个对话的 Direct Line 令牌,请首先从 Azure 门户中的 Direct Line 通道配置页获取 Direct Line 机密。 然后,发出交换 Direct Line 机密的请求,以获取 Direct Line 令牌:
POST https://directline.botframework.azure.cn/v3/directline/tokens/generate
Authorization: Bearer SECRET
在此请求的 Authorization
标头中,将 SECRET 替换为 Direct Line 机密的值。
以下代码段提供了 Generate Token 请求和响应的示例。
请求
POST https://directline.botframework.azure.cn/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"
]
}
参数 | 类型 | 说明 |
---|---|---|
user.id |
字符串 | 可选。 用户的特定于通道的 ID,可以在令牌中编码。 对于 Direct Line 用户,此项必须以 dl_ 开头。 可以为每个聊天创建唯一的用户 ID。为了增加安全性,应该确保此 ID 不可猜测。 |
user.name |
字符串 | 可选。 用户的便于显示的名称,可以在令牌中编码。 |
trustedOrigins |
字符串数组 | 可选。 可以在令牌中嵌入的受信任域的列表。 这些是可以托管机器人网络聊天客户端的域。 这应该与机器人的 Direct Line 配置页中的列表匹配。 |
响应
如果请求成功,响应将包对单个会话有效的 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
),因为这两个操作都返回可用于访问单个会话的 token
。 但是,与启动对话操作不同的是,生成令牌操作不会启动对话、不会联系机器人、也不会创建流式处理 WebSocket URL。
如果计划将令牌分发给客户端并希望它们启动会话,请使用生成令牌操作。 如果打算立即启动会话,请改用启动会话操作。
刷新 Direct Line 令牌
只要 Direct Line 令牌未过期,就可以无次数限制地刷新它。 不能刷新已到期的令牌。 若要刷新 Direct Line 令牌,请发出此请求:
POST https://directline.botframework.azure.cn/v3/directline/tokens/refresh
Authorization: Bearer TOKEN_TO_BE_REFRESHED
在此请求的 Authorization
标头中,将 TOKEN_TO_BE_REFRESHED 替换为你想要刷新的 Direct Line 令牌。
以下代码段提供了 Refresh Token 请求和响应的示例。
请求
POST https://directline.botframework.azure.cn/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 机器人服务身份验证
此部分提供的信息基于通过 Azure AI 机器人服务向机器人添加身份验证一文。
可以通过“Azure AI 机器人服务身份验证”对用户进行身份验证并从各类标识提供者(例如 Microsoft Entra ID、GitHub、Uber 等)处获取“访问令牌”。 也可为自定义 OAuth2 标识提供者配置身份验证。 因此,你可以编写适用于所有受支持的标识提供者和通道的身份验证代码。 若要使用这些功能:
- 以静态方式在机器人上配置
settings
,其中包含应用程序注册到标识提供者时的详细信息。 - 使用在上一步提供的应用程序信息所支持的
OAuthCard
登录用户。 - 通过“Azure AI 机器人服务 API”检索访问令牌。
安全注意事项
将Azure AI 机器人服务身份验证与 Web 聊天配合使用时,必须牢记一些重要的安全注意事项。
模拟。 模拟是攻击者说服机器人,攻击者是其他人。 在 Web 聊天中,攻击者可能会通过“更改 Web 聊天实例的用户 ID”来模拟其他某人。 若要防止这种冒名顶替的情况,建议机器人开发者使用“不可猜出的用户 ID”。
如果启用“增强的身份验证”选项,Azure AI 机器人服务可以进一步检测并拒绝任何用户 ID 更改。 这意味着,从 Direct Line 发送到机器人的消息中的用户 ID (
Activity.From.Id
) 将始终与初始化网上聊天时所用的用户 ID 相同。 此功能要求用户 ID 以dl_
开头。注意
如果在交换令牌的机密时提供 User.Id,该 User.Id 会嵌入到令牌中。 Direct Line 可确保发送到机器人的消息将该 ID 作为活动的 From.Id。如果客户端将消息发送到具有不同 From.Id 的 Direct Line,则在将消息转发到机器人之前,它将更改为“令牌中的 ID”。 因此,使用某个用户 ID 初始化频道机密后,不能使用其他用户 ID
用户标识。 每个用户都有多个用户标识:
- 通道中的用户标识。
- 机器人所关注的标识提供者中的用户标识。
当机器人要求通道中的用户 A 登录标识提供者 P 时,登录过程必须确保用户 A 是登录 P 的用户。如果允许另一用户(用户 B)登录,则用户 A 就可以通过机器人访问用户 B 的资源。 在 Web 聊天中,我们有 2 种机制来确保正确的用户登录,如下文所述。
在过去,当登录结束时,系统会向用户显示一个随机生成的 6 位数代码(幻码)。 用户必须在启动登录的会话中键入该代码,以便完成登录过程。 此机制容易导致糟糕的用户体验。 另外,它容易受到钓鱼攻击。 恶意用户可能会诱骗另一用户登录,然后通过钓鱼获得幻码。
由于前一方法存在问题,Azure AI 机器人服务去除了对幻码的要求。 Azure AI 机器人服务确保登录过程只能在网上聊天本身所在的“浏览器会话”中完成。 若要启用此项保护,你作为机器人开发者必须使用一个 Direct Line 令牌启动网上聊天,该令牌包含可以托管机器人网上聊天客户端的受信任域的列表。 在以前,你只能将未记录的可选参数传递给 Direct Line 令牌 API,以这种方式获取该令牌。 现在,由于有了增强型身份验证选项,你可以在 Direct Line 配置页中以静态方式指定受信任域(源)列表。
有关详细信息,请参阅通过 Azure AI 机器人服务向机器人添加身份验证。
代码示例
以下 .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.azure.cn/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.azure.cn/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');
}
});
});