Как настроить разные аутентификации для API и пользовательского интерфейса - PullRequest
0 голосов
/ 11 октября 2019

Я использую ASP.NetCore 2.2 (возможно, скоро перейду на 3.0). У меня есть приложение службы приложений Azure.

Я хочу иметь API, с помощью которого клиенты будут использовать токен API (секрет клиента) для аутентификации, чтобы они могли работать без интерактивной авторизации.

Для части пользовательского интерфейса потребуется проверка подлинности Azure Active Directory.

Как мне соединить эти два разных метода аутентификации с моим приложением ASP.Net Core?

1 Ответ

0 голосов
/ 11 октября 2019

How-to

  1. Во-первых, нам нужен AuthenticationHandler & Опции для аутентификации запроса с помощью токена API (Client Secret). Предположим, вы создали такой обработчик и параметры аутентификации:

    public class ClientSecretAuthenOpts : AuthenticationSchemeOptions
    {
        public const string DefaultAuthenticationSchemeName = "ClientSecret";
    }
    
    public class ClientSecretAuthenticationHandler : AuthenticationHandler<ClientSecretAuthenOpts>
    {
        public ClientSecretAuthenticationHandler(IOptionsMonitor<ClientSecretAuthenOpts> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) 
            : base(options, logger, encoder, clock) { }
    
        protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
        {
            // ... authenticate request 
        }
    }
    
  2. А затем зарегистрируйте несколько схем аутентификации по одной :

    // add mulitple authentication schemes
    services.AddAuthentication(AzureADDefaults.AuthenticationScheme)           // set AzureAD as the default for users (using the UI)
        .AddAzureAD(options => Configuration.Bind("AzureAD", options))         // setup AzureAD Authentication
        .AddScheme<ClientSecretAuthenOpts,ClientSecretAuthenticationHandler>(  // setup ClientSecret Authentication
            ClientSecretAuthenOpts.DefaultAuthenticationSchemeName,
            opts=>{ }
        );
    
    // post configuration for OIDC
    services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>{
        options.Authority = options.Authority + "/v2.0/";         // Microsoft identity platform
        options.TokenValidationParameters.ValidateIssuer = false; // accept several tenants 
    });
    
  3. Наконец, для одновременного включения нескольких схем аутентификации нам необходимо переопределить политику по умолчанию :

    services.AddAuthorization(opts => {
        // allow AzureAD & our own ClientSecret Authentication at the same time
        var pb = new AuthorizationPolicyBuilder(
            ClientSecretAuthenOpts.DefaultAuthenticationSchemeName,
            "AzureAD"
        );  
        opts.DefaultPolicy = pb.RequireAuthenticatedUser().Build();
    });
    

Демонстрация и тестирование

Предположим, ваш токен API (секрет клиента) отправляется в заголовке запроса, как показано ниже:

GET https://localhost:5001/Home/Privacy HTTP/1.1
<b>Api-Subscription-Id</b>: Smith
<b>Api-Subscription-Key</b>: top secret

Чтобы избежать жесткого кодирования имени заголовка, я создаю addдва свойства в опциях:

public class ClientSecretAuthenOpts : AuthenticationSchemeOptions
{
    public const string DefaultAuthenticationSchemeName = "ClientSecret";
    public string ApiClientIdHeadername {get;set;}= "Api-Subscription-Id";
    public string ApiClientTokenHeaderName {get;set;}= "Api-Subscription-Key";
}

Для аутентификации вышеупомянутого запроса я создаю собственный обработчик аутентификации, как показано ниже:

public class ClientSecretAuthenticationHandler : AuthenticationHandler<ClientSecretAuthenOpts>
{
    public ClientSecretAuthenticationHandler(IOptionsMonitor<ClientSecretAuthenOpts> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) 
        : base(options, logger, encoder, clock) { }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        // if there's no header for Client ID & Client Sercet, skip
        if(
            Context.Request.Headers.TryGetValue(Options.ApiClientIdHeadername, out var clientIdHeader) &&
            Context.Request.Headers.TryGetValue(Options.ApiClientTokenHeaderName, out var clientSecretHeader) 
        ){
            // validate client's id & secret
            var clientId = clientIdHeader.FirstOrDefault();
            var clientKey = clientSecretHeader.FirstOrDefault();
            var (valid, id) = await ValidateApiKeyAsync(clientId, clientKey);

            if(!valid){
                return AuthenticateResult.Fail($"invalid token:{clientKey}");
            }else{
                var principal = new ClaimsPrincipal(id);
                var ticket = new AuthenticationTicket(principal, new AuthenticationProperties(), this.Scheme.Name);
                return AuthenticateResult.Success(ticket);
            }
        }
        return AuthenticateResult.NoResult();
    }

    private Task<(bool, ClaimsIdentity)> ValidateApiKeyAsync(string clientId,string clientSecret)
    {
        ClaimsIdentity id = null;
        // fake: need check key against the Database or other service
        if(clientId=="Smith" && clientSecret == "top secret"){
            id = new ClaimsIdentity(
                new Claim[]{ 
                    new Claim(ClaimTypes.NameIdentifier, "client id from db or from the request"),
                    new Claim("Add Any Claim", "add the value as you like"),
                    // ... 
                }
                ,this.Scheme.Name
            );
            return Task.FromResult((true, id));
        }
        return Task.FromResult((false,id));
    }

}

Test

Допустим, у нас есть действие контроллера, аннотированное атрибутом [Authorize]

[Authorize]
public IActionResult Privacy()
{
    return Ok("hello,world");
}

При доступе к URL-адресу в браузере (пользовательский интерфейс без заголовка) пользователь будет перенаправлен на проверку подлинности Azure AD. если он не вошел в систему.

При тестировании AboМы запросим секрет клиента,

И мы получим ответ "Здравствуй, мир": enter image description here

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