You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Teams 出站WebHook HMAC签名验证不匹配问题求助

Teams WebHook HMAC验证不匹配问题修复

以下是你的代码中存在的关键问题及对应修复方案:

1. 签名编码方式不匹配

Teams生成的HMAC签名采用标准Base64编码,但你的代码使用了WebEncoders.Base64UrlEncode(该编码会替换+为-、/为_并移除末尾=),两者格式不兼容,需改用Convert.ToBase64String生成预期签名。

2. WebHook密钥处理错误

Teams提供的出站WebHook密钥本身是Base64编码字符串,你需要先将其Base64解码为字节数组,而非直接用Encoding.UTF8.GetBytes转换字符串。

3. 请求流未重置

读取完请求体后,需重置请求流的位置,否则后续控制器动作将无法读取请求内容。


修复后的完整代码

protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
    try
    {
        if (!Request.Headers.TryGetValue("Authorization", out var headerValue))
        {
            return AuthenticateResult.Fail("Authorization header not found.");
        }

        var sentSignature = headerValue.ToString().Replace("HMAC ", string.Empty);
        if (string.IsNullOrWhiteSpace(sentSignature))
        {
            return AuthenticateResult.Fail("Invalid Authorization header format.");
        }

        // 读取请求体并重置流位置,保证后续能正常读取
        Request.Body.Position = 0;
        var requestBody = await new StreamReader(Request.Body, Encoding.UTF8).ReadToEndAsync();
        Request.Body.Position = 0;

        if (string.IsNullOrWhiteSpace(requestBody))
        {
            return AuthenticateResult.Fail("No content to authenticate.");
        }

        // 解码Teams提供的Base64格式密钥
        if (!Convert.TryFromBase64String(Options.SecretKey, out var secretKeyBytes, out _))
        {
            return AuthenticateResult.Fail("Invalid WebHook secret key format.");
        }

        using (var hmac = new HMACSHA256(secretKeyBytes))
        {
            var hashBytes = hmac.ComputeHash(Encoding.UTF8.GetBytes(requestBody));
            var expectedSignature = Convert.ToBase64String(hashBytes);

            if (!string.Equals(sentSignature, expectedSignature, StringComparison.Ordinal))
            {
                return AuthenticateResult.Fail("Invalid HMAC signature.");
            }
        }

        var claimsIdentity = new ClaimsIdentity();
        var ticket = new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), Scheme.Name);

        return AuthenticateResult.Success(ticket);
    }
    catch (Exception ex)
    {
        return AuthenticateResult.Fail($"{ex.HResult}, {ex.Message}");
    }
}

额外验证提示

  • 确认使用的密钥是Teams出站WebHook配置页提供的完整Base64字符串,无额外空格或换行。
  • 调试时可打印requestBody、sentSignature和expectedSignature的值,排查是否存在请求体被中间件修改、密钥复制错误等情况。

内容的提问来源于stack exchange,提问作者Aukum

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 05:24:22