.net 如何使Azure Active Directory应用程序角色显示在声明中?

vktxenjb  于 2023-02-17  发布在  .NET
关注(0)|答案(2)|浏览(132)

我正在使用Azure Active Directory向运行Umbraco版本11.0的网站上的后台提供身份验证。
这运行良好,我可以登录,但我想通过使用Azure中的应用角色来管理Umbraco中的用户组来改善体验。

    • 我的Azure设置**

我已使用以下配置在Azure中创建了应用注册:

  • 已添加重定向URI:
  • 统一资源标识符:https://本地主机:44391/
  • 已启用Access tokens (used for implicit flows)
  • 已启用ID tokens (used for implicit and hybrid flows)
  • 支持的帐户类型:Accounts in this organizational directory only (Example only - Single tenant)
  • 添加的应用程序角色
  • 管理人
  • 编辑

在企业应用程序中,我还向我的用户添加了上述应用程序角色:

    • 我的密码**

登录提供程序

namespace Example.Api.Features.Authentication.Extensions;

public static class UmbracoBuilderExtensions
{
    public static IUmbracoBuilder ConfigureAuthentication(this IUmbracoBuilder builder)
    {
        builder.Services.ConfigureOptions<OpenIdConnectBackOfficeExternalLoginProviderOptions>();

        builder.AddBackOfficeExternalLogins(logins =>
        {
            const string schema = MicrosoftAccountDefaults.AuthenticationScheme;
                
            logins.AddBackOfficeLogin(
                backOfficeAuthenticationBuilder =>
                {
                    backOfficeAuthenticationBuilder.AddMicrosoftAccount(
                        // the scheme must be set with this method to work for the back office
                        backOfficeAuthenticationBuilder.SchemeForBackOffice(OpenIdConnectBackOfficeExternalLoginProviderOptions.SchemeName) ?? string.Empty,
                        options =>
                        {
                            //By default this is '/signin-microsoft' but it needs to be changed to this
                            options.CallbackPath = "/umbraco-signin-microsoft/";
                            //Obtained from the AZURE AD B2C WEB APP
                            options.ClientId = "CLIENT_ID";
                            //Obtained from the AZURE AD B2C WEB APP
                            options.ClientSecret = "CLIENT_SECRET";
                            options.TokenEndpoint = $"https://login.microsoftonline.com/TENANT/oauth2/v2.0/token";
                            options.AuthorizationEndpoint = $"https://login.microsoftonline.com/TENANT/oauth2/v2.0/authorize";
                        });
                });
        });

        return builder;
    }
}

自动链接帐户

namespace Example.Api.Features.Configuration;

public class OpenIdConnectBackOfficeExternalLoginProviderOptions : IConfigureNamedOptions<BackOfficeExternalLoginProviderOptions>
{
    public const string SchemeName = "OpenIdConnect";

    public void Configure(string name, BackOfficeExternalLoginProviderOptions options)
    {
        if (name != "Umbraco." + SchemeName)
        {
            return;
        }

        Configure(options);
    }

    public void Configure(BackOfficeExternalLoginProviderOptions options)
    {
        options.AutoLinkOptions = new ExternalSignInAutoLinkOptions(
            // must be true for auto-linking to be enabled
            autoLinkExternalAccount: true,

            // Optionally specify default user group, else
            // assign in the OnAutoLinking callback
            // (default is editor)
            defaultUserGroups: new[] { Constants.Security.EditorGroupAlias },

            // Optionally you can disable the ability to link/unlink
            // manually from within the back office. Set this to false
            // if you don't want the user to unlink from this external
            // provider.
            allowManualLinking: false
        )
        {
            // Optional callback
            OnAutoLinking = (autoLinkUser, loginInfo) =>
            {
                // You can customize the user before it's linked.
                // i.e. Modify the user's groups based on the Claims returned
                // in the externalLogin info

                autoLinkUser.IsApproved = true;
            },
            OnExternalLogin = (user, loginInfo) =>
            {
                // You can customize the user before it's saved whenever they have
                // logged in with the external provider.
                // i.e. Sync the user's name based on the Claims returned
                // in the externalLogin info

                return true; //returns a boolean indicating if sign in should continue or not.
            }
        };

        // Optionally you can disable the ability for users
        // to login with a username/password. If this is set
        // to true, it will disable username/password login
        // even if there are other external login providers installed.
        options.DenyLocalLogin = true;

        // Optionally choose to automatically redirect to the
        // external login provider so the user doesn't have
        // to click the login button. This is
        options.AutoRedirectLoginToExternalProvider = true;
    }
}

在这个文件中,我最好按照注解和i.e. Modify the user's groups based on the Claims returned in the externalLogin info的要求去做。
也在我的启动文件中注册

services.AddUmbraco(_env, _config)
            .AddBackOffice()
            .AddWebsite()
            .AddComposers()
            .ConfigureAuthentication()
            .Build();

我尝试给应用程序以下权限,但没有成功:

目前的情况是,我可以很好地登录,但如果我调试externalInfo,那里没有任何关于用户具有如上所述配置的AdministratorEditor应用程序角色的信息。
我的直觉是,我错过了Azure Active Directory设置的一些东西,但我已经尝试了一些不同的配置,似乎无法让应用程序角色回来。
谢谢你,

    • 编辑日期:2023年2月15日:**

我可以看到,当我使用client_credentials作为grant_type访问https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token端点时,角色返回。它看起来像是. NET应用程序使用authorization_code。我已经解码了从中检索到的令牌,它不包含角色。
我想知道. NET应用程序上是否有某种配置允许我添加角色。

uz75evzq

uz75evzq1#

要在令牌声明中获取应用程序角色,您可以使用客户端凭据流通过授予管理员同意来生成访问令牌。

我尝试通过Postman在我的环境中重现相同的问题,结果如下:

我注册了一个Azure ADWeb应用程序,并创建了如下所示的应用程序角色

现在,我将这些应用程序角色分配给企业应用程序下的用户,如下所示:

在应用程序的API权限中添加这些App角色,如下所示:

您可以在**Application权限下查看应用程序角色**,如下所示:

确保授予管理员许可以上权限,如下所示:

生成访问令牌时,范围应为以/.default结尾的应用程序ID URI

现在,我通过Postman使用客户端凭据流生成了访问令牌,参数如下:

POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token

client_id: <appID>
grant_type:client_credentials
scope: api://<appID>/.default
client_secret: secret
    • 答复:**

当我在jwt.ms中解码上述令牌时,我在**roles声明中成功获得了App roles**,如下所示:

请注意,应用程序角色是应用程序权限,仅适用于不涉及用户交互的流,如客户端凭据。
因此,如果您使用授权代码流、用户名和密码流等委托流,您将无法在令牌声明中获得应用程序角色

    • 更新日期:**

您可以使用以下c#代码从客户端凭据流获取访问令牌,如下所示:

using Microsoft.Identity.Client;

var clientID = "bbb739ad-98a4-4566-8408-dxxxxxxxx3b";
var clientSecret = "K.k8Q~hwtxxxxxxxxxxxxxxxU";
var tenantID = "fb134080-e4d2-45f4-9562-xxxxxx";
var authority = $"https://login.microsoftonline.com/{tenantID}";
var clientApplication = ConfidentialClientApplicationBuilder.Create(clientID)
  .WithClientSecret(clientSecret)
  .WithAuthority(authority)
  .Build();
var scopes = new string[] { "api://bbb739ad-98a4-4566-8408-xxxxxx/.default" };
var authenticationResult = await clientApplication.AcquireTokenForClient(scopes)
  .ExecuteAsync()
  .ConfigureAwait(false);
var accesstoken = authenticationResult.AccessToken;
Console.WriteLine(accesstoken);
    • 答复:**

当我解码上面的令牌时,它具有**roles声明,带有应用程序角色**,如下所示:

46scxncf

46scxncf2#

为了解决这个问题,我最终将AddMicrosoftAccount``AuthenticationBuilder换成了AddOpenIdConnect,这似乎尊重了令牌中的声明。
这就是我现在在ConfigureAuthentication方法中使用的代码。

public static IUmbracoBuilder ConfigureAuthentication(this IUmbracoBuilder builder)
{
    // Register OpenIdConnectBackOfficeExternalLoginProviderOptions here rather than require it in startup
    builder.Services.ConfigureOptions<OpenIdConnectBackOfficeExternalLoginProviderOptions>();

    builder.AddBackOfficeExternalLogins(logins =>
    {
        logins.AddBackOfficeLogin(
            backOfficeAuthenticationBuilder =>
            {
                backOfficeAuthenticationBuilder.AddOpenIdConnect(
                    // The scheme must be set with this method to work for the back office
                    backOfficeAuthenticationBuilder.SchemeForBackOffice(OpenIdConnectBackOfficeExternalLoginProviderOptions.SchemeName),
                    options =>
                    {
                        options.CallbackPath = "/umbraco-signin-microsoft/";
                        // use cookies
                        options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
                        // pass configured options along
                        options.Authority = "https://login.microsoftonline.com/{tenantId}/v2.0";
                        options.ClientId = "{clientId}";
                        options.ClientSecret = "{clientSecret}";
                        // Use the authorization code flow
                        options.ResponseType = OpenIdConnectResponseType.Code;
                        options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
                        // map claims
                        options.TokenValidationParameters.NameClaimType = "name";
                        options.TokenValidationParameters.RoleClaimType = "role";

                        options.RequireHttpsMetadata = true;
                        options.GetClaimsFromUserInfoEndpoint = true;
                        options.SaveTokens = true;
                        options.UsePkce = true;
                        
                        options.Scope.Add("email");
                    });
            });
    });
    return builder;
}

相关问题