具有 ASP.NET 核心的 Azure AD B2C - 无法去编辑配置文件

Azure AD B2C with ASP.NET Core - Unable to go to edit profile

我尝试查找与此相关的问题,但找不到任何内容。

我有一个 ASP.NET Core 1.0 应用程序,它使用 Azure AD B2C 进行身份验证。签名和注册以及注销工作都很好。当我尝试去编辑用户的个人资料时,问题就来了。这是我的 Startup.cs 的样子:

namespace AspNetCoreBtoC
{
    public class Startup
    {
        private IConfigurationRoot Configuration { get; }

        public Startup(IHostingEnvironment env)
        {
            var builder = new ConfigurationBuilder()
                            .SetBasePath(env.ContentRootPath)
                            .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
                            .AddEnvironmentVariables();
            Configuration = builder.Build();
        }

        // This method gets called by the runtime. Use this method to add services to the container.
        // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddSingleton<IConfiguration>(Configuration);
            services.AddMvc();
            services.AddAuthentication(
                opts => opts.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme);
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            loggerFactory.AddConsole();

            if (env.IsDevelopment())
            {
                loggerFactory.AddDebug(LogLevel.Debug);
                app.UseDeveloperExceptionPage();
            }

            app.UseStaticFiles();

            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AutomaticChallenge = false
            });

            string signUpPolicyId = Configuration["AzureAd:SignUpPolicyId"];
            string signUpCallbackPath = Configuration["AzureAd:SignUpCallbackPath"];
            app.UseOpenIdConnectAuthentication(CreateOidConnectOptionsForPolicy(signUpPolicyId, false, signUpCallbackPath));

            string userProfilePolicyId = Configuration["AzureAd:UserProfilePolicyId"];
            string profileCallbackPath = Configuration["AzureAd:ProfileCallbackPath"];
            app.UseOpenIdConnectAuthentication(CreateOidConnectOptionsForPolicy(userProfilePolicyId, false, profileCallbackPath));

            string signInPolicyId = Configuration["AzureAd:SignInPolicyId"];
            string signInCallbackPath = Configuration["AzureAd:SignInCallbackPath"];
            app.UseOpenIdConnectAuthentication(CreateOidConnectOptionsForPolicy(signInPolicyId, true, signInCallbackPath));

            app.UseMvc(routes =>
            {
                routes.MapRoute(
                    name: "Default",
                    template: "{controller=Home}/{action=Index}/{id?}");
            });
        }

        private OpenIdConnectOptions CreateOidConnectOptionsForPolicy(string policyId, bool autoChallenge, string callbackPath)
        {
            string aadInstance = Configuration["AzureAd:AadInstance"];
            string tenant = Configuration["AzureAd:Tenant"];
            string clientId = Configuration["AzureAd:ClientId"];
            string redirectUri = Configuration["AzureAd:RedirectUri"];

            var opts = new OpenIdConnectOptions
            {
                AuthenticationScheme = policyId,
                MetadataAddress = string.Format(aadInstance, tenant, policyId),
                ClientId = clientId,
                PostLogoutRedirectUri = redirectUri,
                ResponseType = "id_token",
                TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name"
                },
                CallbackPath = callbackPath,
                AutomaticChallenge = autoChallenge
            };

            opts.Scope.Add("openid");

            return opts;
        }
    }
}

这是我的 AccountController,我从那里向中间件发出挑战:

namespace AspNetCoreBtoC.Controllers
{
    public class AccountController : Controller
    {
        private readonly IConfiguration config;

        public AccountController(IConfiguration config)
        {
            this.config = config;
        }

        public IActionResult SignIn()
        {
            return Challenge(new AuthenticationProperties
            {
                RedirectUri = "/"
            },
            config["AzureAd:SignInPolicyId"]);
        }

        public IActionResult SignUp()
        {
            return Challenge(new AuthenticationProperties
            {
                RedirectUri = "/"
            },
            config["AzureAd:SignUpPolicyId"]);
        }

        public IActionResult EditProfile()
        {
            return Challenge(new AuthenticationProperties
            {
                RedirectUri = "/"
            },
            config["AzureAd:UserProfilePolicyId"]);
        }

        public IActionResult SignOut()
        {
            string returnUrl = Url.Action(
                action: nameof(SignedOut),
                controller: "Account",
                values: null,
                protocol: Request.Scheme);
            return SignOut(new AuthenticationProperties
            {
                RedirectUri = returnUrl
            },
            config["AzureAd:UserProfilePolicyId"],
            config["AzureAd:SignUpPolicyId"],
            config["AzureAd:SignInPolicyId"],
            CookieAuthenticationDefaults.AuthenticationScheme);
        }

        public IActionResult SignedOut()
        {
            return View();
        }
    }
}

我尝试从 OWIN 示例中改编它。我遇到的问题是,为了去编辑配置文件,我必须向负责该操作的 OpenIdConnect 中间件发出挑战。问题是它调用了默认登录中间件 (Cookies),它意识到用户已通过身份验证,因此该操作一定是未经授权的,并尝试重定向到 /Account/AccessDenied(即使我没有甚至在那条路线上有任何东西),而不是去 Azure AD 编辑配置文件。

有人在 ASP.NET Core 中成功实现了用户配置文件编辑吗?

嗯,我终于解决了。我写了一篇关于设置的博客文章,其中包括解决方案:https://joonasw.net/view/azure-ad-b2c-with-aspnet-core。问题是 ChallengeBehavior,它必须设置为 Unauthorized,而不是默认值 Automatic。目前无法使用框架 ChallengeResult 来定义它,所以我自己做了:

public class MyChallengeResult : IActionResult
{
    private readonly AuthenticationProperties authenticationProperties;
    private readonly string[] authenticationSchemes;
    private readonly ChallengeBehavior challengeBehavior;

    public MyChallengeResult(
        AuthenticationProperties authenticationProperties,
        ChallengeBehavior challengeBehavior,
        string[] authenticationSchemes)
    {
        this.authenticationProperties = authenticationProperties;
        this.challengeBehavior = challengeBehavior;
        this.authenticationSchemes = authenticationSchemes;
    }

    public async Task ExecuteResultAsync(ActionContext context)
    {
        AuthenticationManager authenticationManager =
            context.HttpContext.Authentication;

        foreach (string scheme in authenticationSchemes)
        {
            await authenticationManager.ChallengeAsync(
                scheme,
                authenticationProperties,
                challengeBehavior);
        }
    }
}

对不起这个名字...但是这个可以从控制器操作返回,并且通过指定 ChallengeBehavior.Unauthorized,我让一切正常工作。