Middleware de autenticação ASP.NET Core 2.0

89

Com o Core 1.1, seguiu o conselho de @blockdart e implementou um middleware personalizado:

https://stackoverflow.com/a/31465227/29821

Funcionou assim:

  1. Middleware rodou. Pegou um token dos cabeçalhos de solicitação.
  2. Verificou o token e, se válido, criou uma identidade (ClaimsIdentity) que continha várias declarações que, em seguida, foi adicionada por meio de HttpContext.User.AddIdentity ();
  3. Em ConfigureServices using services.AddAuthorization, adicionei uma política para exigir a declaração fornecida pelo middleware.
  4. Nos controladores / ações, eu usaria [Autorizar (Funções = "alguma função que o middleware adicionou")]

Isso funciona um pouco com 2.0, exceto que se o token não for válido (etapa 2 acima) e a declaração nunca for adicionada, recebo "Nenhum authenticationScheme foi especificado e nenhum DefaultChallengeScheme encontrado."

Agora estou lendo que a autenticação mudou no 2.0:

https://docs.microsoft.com/en-us/aspnet/core/migration/1x-to-2x/identity-2x

Qual é o caminho certo para eu fazer a mesma coisa no ASP.NET Core 2.0? Não vejo um exemplo para fazer autenticação verdadeiramente personalizada.

PBZ
fonte
Experimente este link, embora ele diga 2 esquemas, mas daria a você um alerta
Mithun Pattankar
você poderia adicionar seu código para que possamos dar uma olhada? Eu sei que tive problemas com JWT no core2.0 - foi um caso de movê-lo na inicialização
Webezine

Respostas:

193

Então, depois de um longo dia tentando resolver esse problema, eu finalmente descobri como a Microsoft quer que façamos manipuladores de autenticação personalizados para sua nova configuração de middleware único no núcleo 2.0.

Depois de examinar parte da documentação do MSDN, encontrei uma classe chamada AuthenticationHandler<TOption>que implementa a IAuthenticationHandlerinterface.

A partir daí, encontrei uma base de código inteira com os esquemas de autenticação existentes localizados em https://github.com/aspnet/Security

Dentro de um deles, ele mostra como a Microsoft implementa o esquema de autenticação JwtBearer. ( https://github.com/aspnet/Security/tree/master/src/Microsoft.AspNetCore.Authentication.JwtBearer )

Copiei a maior parte desse código para uma nova pasta e limpei todas as coisas relacionadas com JwtBearer.

Na JwtBearerHandlerclasse (que estende AuthenticationHandler<>), há uma substituição paraTask<AuthenticateResult> HandleAuthenticateAsync()

Eu adicionei nosso middleware antigo para configurar declarações por meio de um servidor de token personalizado e ainda estava encontrando alguns problemas com permissões, apenas emitindo um em 200 OKvez de um 401 Unauthorizedquando um token era inválido e nenhuma declaração foi configurada.

Percebi que havia substituído o Task HandleChallengeAsync(AuthenticationProperties properties)qual, por qualquer motivo, é usado para definir permissões por meio [Authorize(Roles="")]de um controlador.

Depois de remover essa substituição, o código funcionou e lançou um 401quando as permissões não coincidiram.

A principal conclusão disso é que agora você não pode usar um middleware personalizado, você deve implementá-lo via AuthenticationHandler<>e definir o DefaultAuthenticateSchemee DefaultChallengeSchemeao usar services.AddAuthentication(...).

Aqui está um exemplo de como tudo isso deve ser:

Em Startup.cs / ConfigureServices (), adicione:

services.AddAuthentication(options =>
{
    // the scheme name has to match the value we're going to use in AuthenticationBuilder.AddScheme(...)
    options.DefaultAuthenticateScheme = "Custom Scheme";
    options.DefaultChallengeScheme = "Custom Scheme";
})
.AddCustomAuth(o => { });

Em Startup.cs / Configure () adicione:

app.UseAuthentication();

Crie um novo arquivo CustomAuthExtensions.cs

public static class CustomAuthExtensions
{
    public static AuthenticationBuilder AddCustomAuth(this AuthenticationBuilder builder, Action<CustomAuthOptions> configureOptions)
    {
        return builder.AddScheme<CustomAuthOptions, CustomAuthHandler>("Custom Scheme", "Custom Auth", configureOptions);
    }
}

Crie um novo arquivo CustomAuthOptions.cs

public class CustomAuthOptions: AuthenticationSchemeOptions
{
    public CustomAuthOptions()
    {

    }
}

Crie um novo arquivo CustomAuthHandler.cs

internal class CustomAuthHandler : AuthenticationHandler<CustomAuthOptions>
{
    public CustomAuthHandler(IOptionsMonitor<CustomAuthOptions> options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock)
    {
        // store custom services here...
    }
    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        // build the claims and put them in "Context"; you need to import the Microsoft.AspNetCore.Authentication package
        return AuthenticateResult.NoResult();
    }
}
Zac
fonte
1
ótimo post, mas estou tendo alguns problemas ao compilar seu código. Os tipos CustomAuthOptions e AuthenticateResult estão ausentes. Você poderia postar isso?
alexb
8
Você está disposto a compartilhar suas conclusões em código em um repositório Github?
CSharper
2
Você poderia explicar DefaultAuthenticateSchemee DefaultChallengeScheme? Eu não entendo porque eles são usados? e quais são as diferenças entre eles.
Mohammed Noureldin
10
+1 para "A partir daí, encontrei uma base de código inteira com os esquemas de autenticação existentes localizados em github.com/aspnet/Security ." Basta observar como a equipe ASP.NET faz isso enquanto você segue esta resposta (realmente excelente). Algum de nós já pensou que um dia estaríamos fazendo perguntas sobre o código e as práticas do MS e a resposta seria, "basta dar uma olhada em sua base de código?"
Marc L.
3
Para outros que entrarão depois, você AuthExtensionprecisa estar dentro do Microsoft.Extensions.DependencyInjectionnamespace. Veja este exemplo: github.com/aspnet/Security/blob/rel/2.0.0/src/…
Garry Polley
4

Há mudanças consideráveis ​​na identidade do Core 1.x para o Core 2.0, conforme aponta o artigo que você referencia. A principal mudança é se afastar da abordagem de middleware e usar injeção de dependência para configurar serviços personalizados. Isso fornece muito mais flexibilidade na personalização da identidade para implementações mais complexas. Portanto, você deseja se afastar da abordagem de middleware mencionada acima e passar para os serviços. Siga as etapas de migração no artigo referenciado para atingir esse objetivo. Comece substituindo app.UseIdentity por app.UseAuthentication . UseIdentity está depreciado e não terá suporte em versões futuras. Para obter um exemplo completo de como inserir uma transformação de declarações personalizadas e realizar a autorização na declaraçãoveja esta postagem do blog .

Kevin Junghans
fonte
12
Existe um exemplo de como usar isso com um aplicativo WebAPI?
alexb