我能将 ASP.NET Core 登录路由到不同的 Azure B2C 策略吗?

huangapple go评论77阅读模式
英文:

Can I route ASP.NET Core login to different Azure B2C policies?

问题

我有一个ASP.NET Core MVC应用程序,我想根据当前存储在数据库中的用户属性使用2个不同的Azure B2C身份验证策略。可以考虑客户与代理的不同要求作为一个示例。

我知道我可以通过配置绑定一个单一策略,但是否有一种在运行时动态选择策略的方法?

英文:

I have a an ASP.NET Core MVC app that I'd like to use 2 different Azure B2C authentication policies based on a user property currently stored in a database. Think along the lines of different requirements for customers vs agent as an example.

I know I can bind a single policy via config, but is there a way to dynamically pick a policy at runtime?

答案1

得分: 1

是的,假设您正在使用 Microsoft.Identity.Web,那么您的登录链接不应该指向 /MicrosoftIdentity/Account/SignIn,而应该指向您创建的自定义操作,该操作将填充您想要使用的策略名称,例如 /Account/SignIn

[AllowAnonymous]
public class AccountController : Controller
{
    public IActionResult SignIn(string? policy)
    {
        var properties = new AuthenticationProperties
        {
            RedirectUri = this.Url.Content("~/"),
        };

        // 在此示例中,我们允许将策略名称作为参数传递给操作,但这将由您的自定义策略选择逻辑替代
        if (!string.IsNullOrWhiteSpace(policy))
        {
            properties.Items["policy"] = policy;

            // HACK: Microsoft.Identity.Web 在重定向到 IdP 时会删除策略项,但我们需要在使用代码流时更新令牌 URL,所以修复一下...
            properties.Items["customPolicy"] = policy;
        }

        return this.Challenge(properties, OpenIdConnectDefaults.AuthenticationScheme);
    }
}

通过填充 AuthenticationProperties.Items["policy"],Microsoft.Identity.Web 将在重定向到 B2C /authorize 终点时自动使用该策略名称。但是,如果您使用的是 OpenID Connect code 流(应该是这样),您需要确保 /token 终点也使用了正确的策略。

Microsoft.Identity.Web 在 /authorize 调用之后会删除 policy 项目,因此上面的示例还将策略名称设置为 customPolicy,以便在需要交换令牌时可用。只需在 Startup.cs 中更新 Microsoft.Identity.Web 配置,以处理 OnAuthorizationCodeReceived 事件:

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApp(opt =>
        {
            // 其他设置在这里...

            opt.Events.OnAuthorizationCodeReceived += ctx =>
            {
                if (ctx.Properties?.Items.TryGetValue("customPolicy", out var customPolicy) == true)
                {
                    ctx.TokenEndpointRequest.TokenEndpoint = $"{opt.Instance}/{opt.Domain}/{customPolicy}/oauth2/v2.0/token";
                }

                return Task.CompletedTask;
            };
        }/* 如果您要覆盖默认值,则在此处进行 cookie 配置 */);

如果您希望这与 [Authorize] 正确配合使用,那么还需要更改默认的身份验证方案,以便检查您的 Cookie,并更新 Cookie 配置,以使用您的自定义登录 URL 而不是默认的 Microsoft.Identity.Web URL:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApp(opt =>
        {
            // 其他设置在这里...

            opt.Events.OnAuthorizationCodeReceived += ctx =>
            {
                if (ctx.Properties?.Items.TryGetValue("customPolicy", out var customPolicy) == true)
                {
                    ctx.TokenEndpointRequest.TokenEndpoint = $"{opt.Instance}/{opt.Domain}/{customPolicy}/oauth2/v2.0/token";
                }

                return Task.CompletedTask;
            };
        },
        opt =>
        {
            // 这应该是实际的 URL,而不仅仅是控制器和操作名称
            opt.LoginPath = "/Account/SignIn";
            opt.Cookie.SameSite = SameSiteMode.None;
            opt.Cookie.SecurePolicy = CookieSecurePolicy.Always;
            opt.Cookie.IsEssential = true;
        });
英文:

Yes, assuming you're using Microsoft.Identity.Web then instead of your sign-in link going to /MicrosoftIdentity/Account/SignIn have it go to a custom action you create that will populate the policy name you want to use, for example /Account/SignIn:

[AllowAnonymous]
public class AccountController : Controller
{
    public IActionResult SignIn(string? policy)
    {
        var properties = new AuthenticationProperties
        {
            RedirectUri = this.Url.Content("~/"),
        };

        // in this example we're allowing the policy name to be passed in as a parameter to the action, but this would be replaced by your custom policy selection logic
        if (!string.IsNullOrWhiteSpace(policy))
        {
            properties.Items["policy"] = policy;

            // HACK: Microsoft.Identity.Web removes the policy item as part of the redirect to IdP but we need to update the token URL when using code flow, so fix that...
            properties.Items["customPolicy"] = policy;
        }

        return this.Challenge(properties, OpenIdConnectDefaults.AuthenticationScheme);
    }
}

By populating AuthenticationProperties.Items["policy"], Microsoft.Identity.Web will automatically use that policy name when redirecting to the B2C /authorize endpoint. However, if you're using the OpenID Connect code flow (and you should be), you'll need to make sure the /token endpoint also uses the correct policy.

Microsoft.Identity.Web removes the policy item after the /authorize call, so the example above also set the policy name to customPolicy so it's available when tokens need to be exchanged. Simply update your Microsoft.Identity.Web configuration in Startup.cs to also handle the OnAuthorizationCodeReceived event:

services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApp(opt =>
        {
            // other setup here...

            opt.Events.OnAuthorizationCodeReceived += ctx =>
            {
                if (ctx.Properties?.Items.TryGetValue("customPolicy", out var customPolicy) == true)
                {
                    ctx.TokenEndpointRequest.TokenEndpoint = $"{opt.Instance}/{opt.Domain}/{customPolicy}/oauth2/v2.0/token";
                }

                return Task.CompletedTask;
            };
        }/* cookie config happens here if you're overriding defaults */);

If you want this to work correctly with [Authorize] then you also need to change the default authentication scheme so it checks your cookies and update the cookie configuration to use your custom sign-in URL instead of the default Microsoft.Identity.Web one:

services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
        .AddMicrosoftIdentityWebApp(opt =>
        {
            // other setup here...

            opt.Events.OnAuthorizationCodeReceived += ctx =>
            {
                if (ctx.Properties?.Items.TryGetValue("customPolicy", out var customPolicy) == true)
                {
                    ctx.TokenEndpointRequest.TokenEndpoint = $"{opt.Instance}/{opt.Domain}/{customPolicy}/oauth2/v2.0/token";
                }

                return Task.CompletedTask;
            };
        },
        opt =>
        {
            // this should be the actual URL, not just the controller and action name
            opt.LoginPath = "/Account/SignIn";
            opt.Cookie.SameSite = SameSiteMode.None;
            opt.Cookie.SecurePolicy = CookieSecurePolicy.Always;
            opt.Cookie.IsEssential = true;
        });

huangapple
  • 本文由 发表于 2023年7月31日 18:35:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/76802787.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定