Codeblox Blog

ASP.NET Core - Autenticação simples com ApiKey

Guilherme
04/02/2022

Apresentarei aqui uma forma simples de implementar autenticação por ApiKeys no ASP.NET Core.

OpenApi

Caso sua API tenha uma documentação OpenApi e uma interface Swagger para que outros(ou você mesmo) possam testar mais facilmente, é importante informar nela que sua API fornecece esse tipo de autenticação.

Para isso, no Program.cs (ou Startup.cs para templates anteriores ao do .NET6), adicione a configuração:

services.AddSwaggerGen(setup =>
{
    string schemeName = "apiKey";
    setup.AddSecurityDefinition(schemeName, new OpenApiSecurityScheme
    {
        Type = SecuritySchemeType.ApiKey,
        In = ParameterLocation.Header,
        Name = "X-ApiKey",
    });
    setup.AddSecurityRequirement(new OpenApiSecurityRequirement
    {
        { 
            new OpenApiSecurityScheme
            {
                Reference= new OpenApiReference
                {
                    Type= ReferenceType.SecurityScheme,
                    Id= schemeName
                }
            }, 
            Array.Empty<string>() 
        }
    });
    
    // ... outras configs   
}

Tendo isso, você pode verificar que no Swagger UI aparecerá no começo um botão image .

Clicando nele, aparecerá a opção para autorizar com o cabeçalho X-ApiKey, conforme definido no código acima.

image

Autorização

Até então apenas modificamos a documentação OpenApi, isso na prática não adiciona autorização alguma na aplicação.

É necessário criar um Handler de autenticação para autorizar os acessos por ApiKey,

using Microsoft.AspNetCore.Authentication;
using Microsoft.Extensions.Options;
using System.Security.Claims;
using System.Text.Encodings.Web;
namespace MinhaAplicacao;
public class ApiKeyAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
{
    public ApiKeyAuthenticationHandler(
        IOptionsMonitor<AuthenticationSchemeOptions> options,
        ILoggerFactory logger,
        UrlEncoder encoder,
        ISystemClock clock) : base(options, logger, encoder, clock) { }

    protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
    {
        await Task.CompletedTask;
        string apiKey = Request.Headers["X-ApiKey"];

        // Valide a apiKey conforme fizer sentido na sua aplicação
        bool isValidApiKey = ...;

        if (!isValidApiKey) { 
            // Creio não ser possível alterar o Body, então adiciono uma mensagem no cabeçalho para o cliente
            Response.Headers["x-message"] = "Informe ApiKey valida"; // 
            return AuthenticateResult.Fail("É necessário informar uma ApiKey válida"); 
        }

        var claims = new List<Claim>();

        // Adicione claims se fizer sentido para sua aplicação, pode ser que você tenha uma ApiKey para cada cliente.
        //claims.Add(new Claim("clienteId", ...);

        var identity = new ClaimsIdentity(claims, Scheme.Name);
        var principal = new System.Security.Principal.GenericPrincipal(identity, null);
        var ticket = new AuthenticationTicket(principal, Scheme.Name);
        return AuthenticateResult.Success(ticket);
    }
}

Então, registre este Handler de autenticação no Program.cs (ou Startup.cs):

services.AddAuthentication(config => {
    config.AddScheme<ApiKeyAuthenticationHandler>("ApiKey", "API Key");
    // Se ApiKey for o único tipo de autenticão na sua aplicação,
    // acho conveniente utilizar a linha abaixo.
    // Assim é possível usar os atributos [Authorize] sem precisar passar o argumento AuthenticationSchemes.
    config.DefaultScheme = "ApiKey";
});

É necessário garantir que os Middleware de autenticação e autorização do ASP.NET Core sejam utilizados, a ordem aqui é importante:

app.UseAuthentication();
app.UseAuthorization();

app.MapControllers();

Por fim, utilize o atributo [Authorize] nos Controllers ou Actions que deseja proteger. Nos Endpoints correspondentes deve aparecer a figura de um cadeado:

image

dotnet
csharp
dev


Guilherme M. Abdo
Guilherme M. Abdo
Desenvolvedor fullstack .Net/Angular/Azure/GitHub