Warning: file_get_contents(/data/phpspider/zhask/data//catemap/1/ssh/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Asp.net core 如何在OpenID(MSAL)身份验证期间访问数据库_Asp.net Core_Msal_Azure Authentication - Fatal编程技术网

Asp.net core 如何在OpenID(MSAL)身份验证期间访问数据库

Asp.net core 如何在OpenID(MSAL)身份验证期间访问数据库,asp.net-core,msal,azure-authentication,Asp.net Core,Msal,Azure Authentication,我已经使用示例为带有MSAL v2库的Azure AD配置OpendID身份验证。它使用AzureAdAuthenticationBuilderExtensions类来配置OpenId连接事件(代码如下所示) 我想在这些事件中访问我的数据库(EF Core),以检查租户ID并添加一些自定义用户声明。问题在于注入的数据库上下文(services.AddDbContext())是一个作用域服务,无法在Configure方法中调用 public static AuthenticationBuilder

我已经使用示例为带有MSAL v2库的Azure AD配置OpendID身份验证。它使用AzureAdAuthenticationBuilderExtensions类来配置OpenId连接事件(代码如下所示)

我想在这些事件中访问我的数据库(EF Core),以检查租户ID并添加一些自定义用户声明。问题在于注入的数据库上下文(services.AddDbContext())是一个作用域服务,无法在Configure方法中调用

public static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder builder)
        => builder.AddAzureAd(_ => { });

    public static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder builder, Action<AzureAdOptions> configureOptions)
    {
        builder.Services.Configure(configureOptions);
        builder.Services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, ConfigureAzureOptions>();
        builder.AddOpenIdConnect();
        return builder;
    }

    public class ConfigureAzureOptions: IConfigureNamedOptions<OpenIdConnectOptions>
    {
        private readonly AzureAdOptions _azureOptions;

        public AzureAdOptions GetAzureAdOptions() => _azureOptions;

        public ConfigureAzureOptions(IOptions<AzureAdOptions> azureOptions)
        {
            _azureOptions = azureOptions.Value;
        }

        public void Configure(string name, OpenIdConnectOptions options)
        {
            options.ClientId = _azureOptions.ClientId;
            options.Authority = $"{_azureOptions.Instance}{_azureOptions.TenantId}";
            options.UseTokenLifetime = true;
            options.CallbackPath = _azureOptions.CallbackPath;
            options.RequireHttpsMetadata = false;
            options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
            //var allScopes = $"{_azureOptions.Scopes} {_azureOptions.GraphScopes}".Split(new[] {' '});
            var allScopes = $"{_azureOptions.Scopes} https://graph.microsoft.com/.default".Split(new[] { ' ' }); ;
            foreach (var scope in allScopes) { options.Scope.Add(scope); }

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

                // Instead of using the default validation (validating against a single issuer value, as we do in line of business apps),
                // we inject our own multitenant validation logic
                ValidateIssuer = false,

                // If the app is meant to be accessed by entire organizations, add your issuer validation logic here.
                //IssuerValidator = (issuer, securityToken, validationParameters) => {
                //    if (myIssuerValidationLogic(issuer)) return issuer;
                //}
            };

            options.Events = new OpenIdConnectEvents
            {
                OnTicketReceived = context =>
                {
                    return Task.CompletedTask;
                },
                OnAuthenticationFailed = context =>
                {
                    context.Response.Redirect("/Home/Error");
                    Console.WriteLine(context.Exception.Message);
                    context.HandleResponse(); // Suppress the exception
                    return Task.CompletedTask;
                },

                OnAuthorizationCodeReceived = async (context) =>
                {
                    var code = context.ProtocolMessage.Code;
                    var identifier = context.Principal.Claims.FirstOrDefault(c => c.Type.Contains("objectidentifier")).Value;
                    var memoryCache = context.HttpContext.RequestServices.GetRequiredService<IMemoryCache>();
                    //var graphScopes = _azureOptions.GraphScopes.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
                    var graphScopes = new string[] { "https://graph.microsoft.com/.default" };


                    var cca = new ConfidentialClientApplication(
                        _azureOptions.ClientId,
                        $"{_azureOptions.Instance}{_azureOptions.TenantId}",
                        _azureOptions.BaseUrl + _azureOptions.CallbackPath,
                        new ClientCredential(_azureOptions.ClientSecret),
                        new SessionTokenCache(identifier, memoryCache).GetCacheInstance(), 
                        null);
                    //var result = await cca.AcquireTokenByAuthorizationCodeAsync(code, graphScopes);

                    var result = await cca.AcquireTokenByAuthorizationCodeAsync(code, graphScopes);

                    // Check whether the login is from the MSA tenant. 
                    // The sample uses this attribute to disable UI buttons for unsupported operations when the user is logged in with an MSA account.
                    var currentTenantId = context.Principal.Claims.FirstOrDefault(c => c.Type.Contains("tenantid")).Value;
                    if (currentTenantId == "9188040d-6c67-4c5b-b112-36a304b66dad")
                    {
                        // MSA (Microsoft Account) is used to log in
                    }

                    context.HandleCodeRedemption(result.AccessToken, result.IdToken);
                },
                // If your application needs to do authenticate single users, add your user validation below.
                //OnTokenValidated = context =>
                //{
                //    return myUserValidationLogic(context.Ticket.Principal);
                //}
            };
        }

        public void Configure(OpenIdConnectOptions options)
        {
            Configure(Options.DefaultName, options);
        }
    }
}
public static AuthenticationBuilder AddAzureAd(此AuthenticationBuilder)
=>builder.AddAzureAd({});
公共静态AuthenticationBuilder AddAzureAd(此AuthenticationBuilder,操作配置选项)
{
builder.Services.Configure(配置选项);
builder.Services.AddSingleton();
AddOpenIdConnect();
返回生成器;
}
公共类配置AzureOptions:IConfigureNamedOptions
{
私人只读AzureAdOptions _azureOptions;
公共AzureAdOptions GetAzureAdOptions()=>\u azureOptions;
公共配置azureOptions(IOptions azureOptions)
{
_azureOptions=azureOptions.Value;
}
public void配置(字符串名称、OpenIdConnectOptions选项)
{
options.ClientId=\u azureOptions.ClientId;
options.Authority=$“{u-azureOptions.Instance}{{u-azureOptions.TenantId}”;
options.UseTokenLifetime=true;
options.CallbackPath=\u azureOptions.CallbackPath;
options.RequireHttpsMetadata=false;
options.ResponseType=OpenIdConnectResponseType.CodeIdToken;
//var allScopes=$“{u-azureOptions.Scopes}{{u-azureOptions.GraphScopes}”.Split(新[]{''});
var allScopes=$“{u azureOptions.Scopes}https://graph.microsoft.com/.default.Split(新[]{''});
foreach(所有作用域中的var作用域){options.scope.Add(scope);}
options.TokenValidationParameters=新的TokenValidationParameters
{
//确保在登录后正确设置User.Identity.Name
NameClaimType=“name”,
//而不是使用默认验证(针对单个发卡行值进行验证,就像我们在业务线应用程序中所做的那样),
//我们注入自己的多租户验证逻辑
validateisuer=false,
//如果整个组织都要访问该应用程序,请在此处添加您的发卡机构验证逻辑。
//IssuerValidator=(发卡机构、securityToken、validationParameters)=>{
//if(myIssuerValidationLogic(发卡机构))返回发卡机构;
//}
};
options.Events=新的OpenIdConnectEvents
{
OnTicketReceived=context=>
{
返回Task.CompletedTask;
},
OnAuthenticationFailed=上下文=>
{
Redirect(“/Home/Error”);
Console.WriteLine(context.Exception.Message);
context.HandleResponse();//抑制异常
返回Task.CompletedTask;
},
OnAuthorizationCodeReceived=async(上下文)=>
{
var code=context.ProtocolMessage.code;
var identifier=context.Principal.Claims.FirstOrDefault(c=>c.Type.Contains(“objectidentifier”)).Value;
var memoryCache=context.HttpContext.RequestServices.GetRequiredService();
//var graphScopes=_azureOptions.graphScopes.Split(新[]{''},StringSplitOptions.RemoveEmptyEntries);
var graphScopes=新字符串[]{”https://graph.microsoft.com/.default" };
var cca=新的机密客户端应用程序(
_azureOptions.ClientId,
$“{u-azureOptions.Instance}{u-azureOptions.TenantId}”,
_azureOptions.BaseUrl+\u azureOptions.CallbackPath,
新客户端凭据(_azureOptions.ClientSecret),
新建SessionTokenCache(标识符,memoryCache).GetCacheInstance(),
无效);
//var结果=等待cca.AcquireTokenByAuthorizationCodeAsync(代码,图形范围);
var结果=等待cca.AcquireTokenByAuthorizationCodeAsync(代码,图形范围);
//检查登录名是否来自MSA租户。
//当用户使用MSA帐户登录时,此示例使用此属性禁用UI按钮以执行不受支持的操作。
var currentTenantId=context.Principal.Claims.FirstOrDefault(c=>c.Type.Contains(“tenantid”)).Value;
如果(当前租户==“918804D-6c67-4c5b-b112-36a304b66dad”)
{
//MSA(Microsoft帐户)用于登录
}
HandleCodeRedemption(result.AccessToken、result.IdToken);
},
//如果应用程序需要对单个用户进行身份验证,请在下面添加用户验证。
//OnTokenValidated=上下文=>
//{
//返回myUserValidationLogic(context.Ticket.Principal);
//}
};
}
public void配置(OpenIdConnectOptions选项)
{
配置(Options.DefaultName,Options);
}
}
}

您可以创建一个新方法来配置服务,而不是在Configure方法中调用它,如您引用的同一示例上的注释所示

public void ConfigureServices(IServiceCollection services)
  {
    services.AddDbContextPool<ApplicationDbContext>(options => 
      options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
  }
public void配置服务(IServiceCollection服务)
{
services.AddDbContextPool似乎与您的有类似的问题。请
public void Configure(string name, OpenIdConnectOptions options)
{
    //your code
    options.Events = new OpenIdConnectEvents
    {
        OnTicketReceived = context =>
        {
            var db = context.HttpContext.RequestServices.GetRequiredService<ApplicationDbContext>();
            // If your authentication logic is based on users then add your logic here
            return Task.CompletedTask;
        },
        OnAuthenticationFailed = context =>
        {
            var db = context.HttpContext.RequestServices.GetRequiredService<ApplicationDbContext>();
            context.Response.Redirect("/Home/Error");
            context.HandleResponse(); // Suppress the exception
            return Task.CompletedTask;
        },
        OnAuthorizationCodeReceived = async (context) =>
        {
            var db = context.HttpContext.RequestServices.GetRequiredService<ApplicationDbContext>();
            var code = context.ProtocolMessage.Code;
            var identifier = context.Principal.FindFirst(Startup.ObjectIdentifierType).Value;
            var memoryCache = context.HttpContext.RequestServices.GetRequiredService<IMemoryCache>();
            var graphScopes = _azureOptions.GraphScopes.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);

            var cca = new ConfidentialClientApplication(
                _azureOptions.ClientId, 
                _azureOptions.BaseUrl + _azureOptions.CallbackPath,
                new ClientCredential(_azureOptions.ClientSecret),
                new SessionTokenCache(identifier, memoryCache).GetCacheInstance(), 
                null);
            var result = await cca.AcquireTokenByAuthorizationCodeAsync(code, graphScopes);

            // Check whether the login is from the MSA tenant. 
            // The sample uses this attribute to disable UI buttons for unsupported operations when the user is logged in with an MSA account.
            var currentTenantId = context.Principal.FindFirst(Startup.TenantIdType).Value;
            if (currentTenantId == "9188040d-6c67-4c5b-b112-36a304b66dad")
            {
                // MSA (Microsoft Account) is used to log in
            }

            context.HandleCodeRedemption(result.AccessToken, result.IdToken);
        },
        // If your application needs to do authenticate single users, add your user validation below.
        //OnTokenValidated = context =>
        //{
        //    return myUserValidationLogic(context.Ticket.Principal);
        //}
    };
}