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

Azure B2C按需为用户创建应用注册的方案是否可行?客户端类型如何选?

问题背景与疑问

我有一个简单的ASP.NET Core Razor Pages应用 + ASP.NET Core API组合:

  • API配置代码:
builder.Services
    .AddAuthentication(...)
    .AddMicrosoftIdentityWebApi()
  • Razor Pages配置代码:
builder.Services
    .AddMicrosoftIdentityWebApp(azureConfig)
    .EnableTokenAcquisitionToCallDownstreamApi(...)
    .AddInMemoryTokenCaches();

目前已经创建了两个Azure AD应用注册,分别对应UI和对外提供服务的API。

我想开发一个UI功能,让用户能生成自己专属的客户端。根据经验,同类应用通常只支持客户端凭据类型的客户端。

我设想的后台逻辑如下:
点击“Create new client”按钮后的后台代码:

public async Task CreateClientForUser(CurrentUser currentUser)
{
    var graphClient = new GraphServiceClient(requestAdapter);

    var requestBody = new Application
    {
        DisplayName = "API Key for " + currentUser.DisplayName,
    };
    var result = await graphClient.Applications.PostAsync(requestBody);

    dbContext.UserApps.Add(new UserApp
    {
        UserId = currentUser.Id,
        AppId = result.AppId
    });
    await dbContext.SaveChangesAsync();
}

同时计划在API中添加OnTokenValidated事件处理:

// 未测试的伪代码
services.Configure<JwtBearerOptions>(
    name: JwtBearerDefaults.AuthenticationScheme,
    configureOptions: jwtBearerOptions =>
{
    var existingOnTokenValidatedHandler = jwtBearerOptions.Events.OnTokenValidated;
    jwtBearerOptions.Events.OnTokenValidated = async tokenValidatedContext =>
    {
        await existingOnTokenValidatedHandler(tokenValidatedContext);

        var context = tokenValidatedContext.HttpContext.RequestServices.GetRequiredService<BOIDbContext>();

        var principal = tokenValidatedContext.Principal!;
        var claimsIdentity = (ClaimsIdentity)principal.Identity!;

        var objectidentifierClaimType = "http://schemas.microsoft.com/identity/claims/objectidentifier";
        var objectIdentifier = principal.Claims.FirstOrDefault(t => t.Type == objectidentifierClaimType);

        var appIdClaim = principal.Claims.FirstOrDefault(t => t.Type == "appId");

        if (appIdClaim?.Value != "Main RazorPages Web UI")
        {
            // 这应该是用户的访问令牌?
            var usersApp = context.UserApps.FirstOrDefault(x => x.AppId == appIdClaim.Value);
            if(usersApp is null)
            {
                tokenValidatedContext.Fail($"No app id found in app-user mapping table.");
                return;
            }

            // 设置用户ID
            claimsIdentity.AddClaim(new Claim(ApplicationClaimTypes.UserId, usersApp.UserId.ToString()));
        }
    };
});

现在有几个疑问:

  1. 这个方案是否可行?
  2. 多数提供API访问的网站仅允许生成客户端凭据类型的客户端,我是否也应该只允许用户创建此类客户端?
  3. 如果改为允许创建交互式客户端,是不是就不需要维护用户映射表了?

回答

1. 方案可行性分析

整体思路是可行的,但有几个细节需要修正和注意:

  • 代码修正:原CreateClientForUser方法缺少async/await关键字,且未调用SaveChangesAsync保存数据库变更,需要补上(已在上方代码示例中修正)。
  • 令牌类型区分:不要仅通过appId判断令牌类型,客户端凭据模式的令牌不会包含objectidentifier声明,而用户交互式令牌会携带该声明,可通过这一点明确区分两种令牌,避免逻辑混淆。
  • Graph API权限:用Graph API创建应用注册需要对应的应用权限(比如Application.ReadWrite.OwnedBy),要确保Razor Pages应用注册已被授予该权限,且需设置为应用权限而非委托权限,因为创建应用是后台操作,无需用户交互。
  • 应用所有权设置:创建应用时,建议将当前用户设为该应用的所有者,方便用户后续管理(比如删除),可在Application对象中添加Owners属性并传入当前用户的objectId。

2. 是否仅允许客户端凭据类型的客户端

建议优先支持客户端凭据类型,原因如下:

  • 安全可控:客户端凭据模式下,客户端权限独立,可通过API权限精确控制其访问范围;同时通过映射表关联到用户,既实现归属管理,又避免令牌与特定用户绑定,适合服务端集成、自动化脚本等场景。
  • 符合用户预期:正如你观察到的,多数同类平台都采用这种模式,用户更容易理解和上手。
  • 降低复杂度:支持多种客户端类型需要处理不同的令牌验证逻辑,比如交互式令牌的用户身份校验、刷新令牌管理等,会显著增加系统复杂度,初期聚焦客户端凭据是更稳妥的选择。

如果后续用户有明确的交互式客户端需求(如桌面APP、移动端APP),再考虑扩展支持即可。

3. 交互式客户端是否需要维护映射表

分两种情况判断:

  • 用户自用的交互式客户端:用户通过该客户端登录后,API会直接拿到用户的objectidentifier声明,此时无需映射表,直接用该声明关联系统用户即可。
  • 用户创建的第三方交互式客户端:这种情况下,仍需映射表记录客户端与创建者的关联,用于权限审计、客户端生命周期管理(比如用户删除时同步删除客户端),但身份验证阶段不需要依赖映射表,因为令牌已包含当前登录用户的信息。

需要注意的是,交互式客户端的令牌是用户委托的,权限范围受用户自身权限限制;而客户端凭据令牌是应用级别的,两者安全模型不同,需分开设计验证逻辑。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 20:35:02