使用Authentication.AzureAD.UI库时实现OpenIdConnectOptions事件

Dan*_*ski 7 c# azure-active-directory openid-connect asp.net-core

我一直在使用从示例创建的库,该库允许我使用Azure Active Directory验证.NET核心Web应用并利用各种OpenIdConnectOptions事件(例如OnTokenValidated)向主体添加某些声明,并将该数据添加到类似于身份的数据库,因此API可以基于调用者的令牌对调用者进行基于策略的确定。

但是我宁愿使用Microsoft.AspNetCore.Authentication.AzureAD.UINuGet软件包,也不愿使用我的自定义版本,我只是不确定如何访问和访问事件OpenIdConnectOptions

我不知道这不是可以完成的事情,还是我对依赖注入没有足够的了解以了解如何做到这一点。

还是我应该考虑在流程的其他部分添加索赔等?

public static AuthenticationBuilder AddAzureAD(
    this AuthenticationBuilder builder,
    string scheme,
    string openIdConnectScheme,
    string cookieScheme,
    string displayName,
    Action<AzureADOptions> configureOptions) {

    AddAdditionalMvcApplicationParts(builder.Services);
    builder.AddPolicyScheme(scheme, displayName, o => {
        o.ForwardDefault = cookieScheme;
        o.ForwardChallenge = openIdConnectScheme;
    });

    builder.Services.Configure(
        TryAddOpenIDCookieSchemeMappings(scheme, openIdConnectScheme, cookieScheme));

    builder.Services.TryAddSingleton<IConfigureOptions<AzureADOptions>, AzureADOptionsConfiguration>();

    // They put in their custom OpenIdConnect configuration, but I can't see how to get at the events.
    builder.Services.TryAddSingleton<IConfigureOptions<OpenIdConnectOptions>, OpenIdConnectOptionsConfiguration>();

    builder.Services.TryAddSingleton<IConfigureOptions<CookieAuthenticationOptions>, CookieOptionsConfiguration>();

    builder.Services.Configure(scheme, configureOptions);

    builder.AddOpenIdConnect(openIdConnectScheme, null, o => { });
    builder.AddCookie(cookieScheme, null, o => { });

    return builder;
}
Run Code Online (Sandbox Code Playgroud)

mth*_*rba 10

我可能在这里参加聚会有点晚,但是我遇到了同样的问题,发现AzureAD身份验证中间件的文档非常稀疏。在这里为其他遇到相同问题的人添加解决方案。

正如你可以在这个问题的代码片段的底部看到,AzureAD提供商实际上依赖于OpenIdConnectCookie头套下AUTH提供商,并没有实现任何验证逻辑本身。

为此,添加了两个附加的身份验证方案,分别使用定义为AzureADDefaults.OpenIdScheme和的名称AzureADDefaults.CookieScheme

(尽管使用AddAzureAD(this Microsoft.AspNetCore.Authentication.AuthenticationBuilder builder, string scheme, string openIdConnectScheme, string cookieScheme, string displayName, Action<Microsoft.AspNetCore.Authentication.AzureAD.UI.AzureADOptions> configureOptions)重载时也可以自定义名称)。

进而允许配置有效的名称,OpenIdConnectOptionsCookieAuthenticationOptions使用上方的方案名称,包括访问OpenIdConnectEvents

请参阅以下完整示例:

        services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
            .AddAzureAD(options => Configuration.Bind("AzureAd", options));

        services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
        {
            options.Events = new OpenIdConnectEvents
            {
                OnRedirectToIdentityProvider = async ctxt =>
                {
                    // Invoked before redirecting to the identity provider to authenticate. This can be used to set ProtocolMessage.State
                    // that will be persisted through the authentication process. The ProtocolMessage can also be used to add or customize
                    // parameters sent to the identity provider.
                    await Task.Yield();
                },
                OnMessageReceived = async ctxt =>
                {
                    // Invoked when a protocol message is first received.
                    await Task.Yield();
                },
                OnTicketReceived = async ctxt =>
                {
                    // Invoked after the remote ticket has been received.
                    // Can be used to modify the Principal before it is passed to the Cookie scheme for sign-in.
                    // This example removes all 'groups' claims from the Principal (assuming the AAD app has been configured
                    // with "groupMembershipClaims": "SecurityGroup"). Group memberships can be checked here and turned into
                    // roles, to be persisted in the cookie.
                    if (ctxt.Principal.Identity is ClaimsIdentity identity)
                    {
                        ctxt.Principal.FindAll(x => x.Type == "groups")
                            .ToList()
                            .ForEach(identity.RemoveClaim);
                    }                        
                    await Task.Yield();
                },
            };
        });

        services.Configure<CookieAuthenticationOptions>(AzureADDefaults.CookieScheme, options =>
        {
            options.Events = new CookieAuthenticationEvents
            {
                // ...
            };
        });
Run Code Online (Sandbox Code Playgroud)

  • 如果您找到这个精彩的答案并且正在使用 Azure B2C 代码(从 Visual Studio 模板创建),只需将“AzureADDefaults.OpenIdScheme”更改为“AzureADB2CDefaults.OpenIdScheme”,这也对您有用:) (3认同)

Gol*_*Age 8

我认为基于Microsoft 官方代码库,您还可以创建一个单独的处理程序类,如下所示:

public class AzureAdOpendIdHandler : IConfigureNamedOptions<OpenIdConnectOptions>
{
    private readonly AzureAdConfig _azureOptions;
    readonly IMediator _mediator;

    public AzureAdConfig GetAzureAdConfig() => _azureOptions;

    public AzureAdOpendIdHandler(IOptions<SiteConfig> siteConfig, IMediator mediator)
    {
        _azureOptions = siteConfig.Value.AzureAdConfig;
        _mediator = mediator;
    }

    public void Configure(string name, OpenIdConnectOptions options)
    {
        options.ClientId = _azureOptions.ClientId;
        options.UseTokenLifetime = true;
        options.CallbackPath = _azureOptions.CallbackPath;
        options.RequireHttpsMetadata = false;
        options.ResponseType = OpenIdConnectResponseType.CodeIdToken;

        options.TokenValidationParameters = new TokenValidationParameters
        {
            // Ensure that User.Identity.Name is set correctly after login
            NameClaimType = "name",
            ValidateIssuer = false,
        };

        options.Events = new OpenIdConnectEvents
        {
            OnTokenValidated = context =>
            {
                var idToken = context.SecurityToken;
                string userIdentifier = idToken.Subject;
                string userEmail =
                    idToken.Claims.SingleOrDefault(c => c.Type == JwtRegisteredClaimNames.Email)?.Value
                    ?? idToken.Claims.SingleOrDefault(c => c.Type == "preferred_username")?.Value;

                string firstName = idToken.Claims.SingleOrDefault(c => c.Type == JwtRegisteredClaimNames.GivenName)?.Value;
                string lastName = idToken.Claims.SingleOrDefault(c => c.Type == JwtRegisteredClaimNames.FamilyName)?.Value;
                string name = idToken.Claims.SingleOrDefault(c => c.Type == "name")?.Value;

                // manage roles, modify token and claims etc.

                return Task.CompletedTask;
            },
            OnTicketReceived = context =>
            {
                // If your authentication logic is based on users then add your logic here
                return Task.CompletedTask;
            },
            OnAuthenticationFailed = context =>
            {
                context.Response.Redirect("/Home/Error");
                context.HandleResponse(); // Suppress the exception
                return Task.CompletedTask;
            },
        };
    }

    public void Configure(OpenIdConnectOptions options)
    {
        Configure(Options.DefaultName, options);
    }
}
Run Code Online (Sandbox Code Playgroud)

ConfigureServices(IServiceCollection services)然后你可以在你的 Startup.cs 中靠近方法末尾的地方注册它

像这样:

services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
                .AddAzureAD(options => Configuration.Bind(nameof(AzureAdConfig), options));

// registration of other services
services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, AzureAdOpendIdHandler>();
Run Code Online (Sandbox Code Playgroud)

这样,您就可以在处理程序中运行一些更复杂的逻辑,因为options.Events您可以注入任何您想要的服务。