Как получить доступ к базе данных во время аутентификации OpenID (MSAL) - PullRequest
0 голосов
/ 04 января 2019

Я использовал этот пример для настройки аутентификации OpendID для Azure AD с библиотекой MSAL v2.Он использует класс AzureAdAuthenticationBuilderExtensions для настройки событий подключения OpenId (код показан ниже).

Я хочу получить доступ к своей базе данных (EF Core) в рамках этих событий, чтобы проверить tenantId и добавить некоторые пользовательские заявки.Проблема заключается в том, что внедренный контекст базы данных (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);
        }
    }
}

Ответы [ 2 ]

0 голосов
/ 07 января 2019

Для доступа к DbContext, вы можете попробовать HttpContext.RequestServices.

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);
        //}
    };
}
0 голосов
/ 05 января 2019

Вместо вызова его в методе Configure вы можете создать новый метод для настройки служб, как показано в комментариях к тому же самому образцу, на который вы ссылались.

public void ConfigureServices(IServiceCollection services)
  {
    services.AddDbContextPool<ApplicationDbContext>(options => 
      options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
  }

У пользователя в этой проблеме Github , похоже, есть проблема, аналогичная вашей.Посмотрите, поможет ли разговор здесь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...