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

如何基于ADB2C实现支持API Connector的C# Web API认证端点?

C# Web API对接ADB2C实现用户名密码认证(支持API Connector,替代ROPC)

核心方案

放弃ROPC流,改用ADB2C的标准授权码流,通过后端模拟用户登录流程:接收客户端提交的用户名密码后,向ADB2C认证端点提交表单获取授权码,再兑换成Bearer Token。这种方式会触发完整的用户流流程,支持API Connector,且符合OAuth2安全规范。

实现步骤

1. ADB2C前置配置

  • 创建支持API Connector的Sign Up/Sign In用户流(如B2C_1_signupsignin),并在用户流中关联你的API Connector(配置在登录前后的验证步骤)
  • 注册后端API应用,记录ClientId、ClientSecret,添加有效的RedirectUri(后端回调地址,示例:https://yourapi.com/api/auth/callback)
  • 确认用户流的Authority地址:https://<tenant-name>.b2clogin.com/tfp/<tenant-name>.onmicrosoft.com/<user-flow-name>/v2.0/

2. 项目依赖

无需额外安装NuGet包,System.Net.Http和System.Text.Json已默认包含在.NET Core/.NET 5+项目中。

3. 编写认证端点代码

using System.Net.Http;
using System.Text;
using System.Text.Json;
using Microsoft.AspNetCore.Mvc;

[ApiController]
[Route("api/auth")]
public class AuthController : ControllerBase
{
    private readonly IConfiguration _config;
    private readonly HttpClient _httpClient;

    public AuthController(IConfiguration config, HttpClient httpClient)
    {
        _config = config;
        _httpClient = httpClient;
    }

    [HttpPost("token")]
    public async Task<IActionResult> ExchangeCredentialsForToken([FromBody] LoginRequest request)
    {
        // 读取ADB2C配置
        var tenant = _config["ADB2C:Tenant"];
        var userFlow = _config["ADB2C:UserFlow"];
        var clientId = _config["ADB2C:ClientId"];
        var clientSecret = _config["ADB2C:ClientSecret"];
        var redirectUri = _config["ADB2C:RedirectUri"];
        var apiScope = _config["ADB2C:ApiScope"];

        // 步骤1:提交用户名密码到ADB2C,获取授权码
        var authEndpoint = $"https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{userFlow}/oauth2/v2.0/authorize";
        var authForm = new FormUrlEncodedContent(new Dictionary<string, string>
        {
            ["client_id"] = clientId,
            ["response_type"] = "code",
            ["redirect_uri"] = redirectUri,
            ["scope"] = $"openid {apiScope}",
            ["username"] = request.Username,
            ["password"] = request.Password,
            ["nonce"] = Guid.NewGuid().ToString("N"),
            ["state"] = Guid.NewGuid().ToString("N")
        });

        var authResponse = await _httpClient.PostAsync(authEndpoint, authForm);
        if (!authResponse.IsSuccessStatusCode)
        {
            var errorDetails = await authResponse.Content.ReadAsStringAsync();
            return BadRequest(new { Error = "ADB2C authentication failed", Details = errorDetails });
        }

        // 从重定向地址中提取授权码
        var redirectLocation = authResponse.Headers.Location.ToString();
        var code = System.Web.HttpUtility.ParseQueryString(new Uri(redirectLocation).Query)["code"];
        if (string.IsNullOrWhiteSpace(code))
        {
            return BadRequest(new { Error = "Failed to retrieve authorization code" });
        }

        // 步骤2:用授权码兑换Bearer Token
        var tokenEndpoint = $"https://{tenant}.b2clogin.com/{tenant}.onmicrosoft.com/{userFlow}/oauth2/v2.0/token";
        var tokenForm = new FormUrlEncodedContent(new Dictionary<string, string>
        {
            ["client_id"] = clientId,
            ["client_secret"] = clientSecret,
            ["code"] = code,
            ["redirect_uri"] = redirectUri,
            ["grant_type"] = "authorization_code"
        });

        var tokenResponse = await _httpClient.PostAsync(tokenEndpoint, tokenForm);
        if (!tokenResponse.IsSuccessStatusCode)
        {
            var errorDetails = await tokenResponse.Content.ReadAsStringAsync();
            return BadRequest(new { Error = "Failed to exchange code for token", Details = errorDetails });
        }

        var tokenResult = await JsonSerializer.DeserializeAsync<TokenResponse>(await tokenResponse.Content.ReadAsStreamAsync());
        return Ok(tokenResult);
    }
}

// 请求模型
public class LoginRequest
{
    public string Username { get; set; }
    public string Password { get; set; }
}

// Token响应模型
public class TokenResponse
{
    [JsonPropertyName("access_token")]
    public string AccessToken { get; set; }

    [JsonPropertyName("token_type")]
    public string TokenType { get; set; }

    [JsonPropertyName("expires_in")]
    public int ExpiresIn { get; set; }

    [JsonPropertyName("id_token")]
    public string IdToken { get; set; }
}

4. 关键注意事项

  • API Connector触发逻辑:此流程会完整执行ADB2C用户流,包括你配置的API Connector验证步骤(比如用户登录前的自定义校验)
  • 安全规范:
    • 必须使用HTTPS传输所有请求,避免用户名密码或Token泄露
    • 验证state参数(可存储在会话或缓存中,对比返回的state),防止CSRF攻击
    • 给认证端点添加速率限制,抵御暴力破解
  • 配置验证:确保RedirectUri已在ADB2C应用注册中添加为有效地址,API权限已正确授予
  • 错误处理:ADB2C会在API Connector验证失败时返回结构化错误信息,可解析后返回给客户端,便于前端展示

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.10 01:04:59