Skip to content

Instantly share code, notes, and snippets.

@dj-nitehawk
Last active March 5, 2024 15:17
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save dj-nitehawk/4efe5ef70f813aec2c55fff3bbb833c0 to your computer and use it in GitHub Desktop.
Save dj-nitehawk/4efe5ef70f813aec2c55fff3bbb833c0 to your computer and use it in GitHub Desktop.
API Key Authentication With FastEndpoints + Swagger
sealed class ApikeyAuth(IOptionsMonitor<AuthenticationSchemeOptions> options,
ILoggerFactory logger,
UrlEncoder encoder,
IConfiguration config)
: AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)
{
internal const string SchemeName = "ApiKey";
internal const string HeaderName = "x-api-key";
readonly string _apiKey = config["Auth:ApiKey"] ?? throw new InvalidOperationException("Api key not set in appsettings.json");
protected override Task<AuthenticateResult> HandleAuthenticateAsync()
{
Request.Headers.TryGetValue(HeaderName, out var extractedApiKey);
if (!IsPublicEndpoint() && !extractedApiKey.Equals(_apiKey))
return Task.FromResult(AuthenticateResult.Fail("Invalid API credentials!"));
var identity = new ClaimsIdentity(
claims: new[] { new Claim("ClientID", "Default") },
authenticationType: Scheme.Name);
var principal = new GenericPrincipal(identity, roles: null);
var ticket = new AuthenticationTicket(principal, Scheme.Name);
return Task.FromResult(AuthenticateResult.Success(ticket));
}
bool IsPublicEndpoint()
=> Context.GetEndpoint()?.Metadata.OfType<AllowAnonymousAttribute>().Any() is null or true;
}
sealed class Endpoint : EndpointWithoutRequest
{
public override void Configure()
{
Get("/protected");
}
public override async Task HandleAsync(CancellationToken ct)
{
await SendAsync("you are authorized!");
}
}
using FastEndpoints;
using FastEndpoints.Swagger;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Options;
using NSwag;
using System.Security.Claims;
using System.Security.Principal;
using System.Text.Encodings.Web;
var bld = WebApplication.CreateBuilder();
bld.Services
.AddFastEndpoints()
.AddAuthorization()
.AddAuthentication(ApikeyAuth.SchemeName)
.AddScheme<AuthenticationSchemeOptions, ApikeyAuth>(ApikeyAuth.SchemeName, null);
bld.Services
.SwaggerDocument(o =>
{
o.EnableJWTBearerAuth = false;
o.DocumentSettings = s =>
{
s.AddAuth(ApikeyAuth.SchemeName, new()
{
Name = ApikeyAuth.HeaderName,
In = OpenApiSecurityApiKeyLocation.Header,
Type = OpenApiSecuritySchemeType.ApiKey,
});
};
});
var app = bld.Build();
app.UseAuthentication()
.UseAuthorization()
.UseFastEndpoints()
.UseSwaggerGen();
app.Run();
@GrantByrn3
Copy link

Thank you so much for writing this. This helped me so much.

@dj-nitehawk
Copy link
Author

@GrantByrn3 you are most welcome mate ;-)

@Adebola92
Copy link

not working for me i keep getting error 401

@dj-nitehawk
Copy link
Author

@Adebola92
Copy link

its working now for me but i added this on my enpoint class [Authorize(AuthenticationSchemes = ApikeyAuth.SchemeName)]

@securitygg
Copy link

It's working for me with adding UseAuthentication() in Program.cs
app.UseAuthentication() //=> Add this
.UseAuthorization()
.UseFastEndpoints()
.UseSwaggerGen();

@paulbreuler
Copy link

You saved me so much time, thank you!

Had to make a minor change due to a deprecation of ISystemClock usage. Just removed from ctor call as seen below.

public sealed class ApikeyAuth(
    IOptionsMonitor<AuthenticationSchemeOptions> options,
    ILoggerFactory logger,
    UrlEncoder encoder,
    IConfiguration config)
    : AuthenticationHandler<AuthenticationSchemeOptions>(options, logger, encoder)

Issue: ISystemClock is obsolete

Solution to use ctor below from Microsoft.AspNetCore.Authentication abstract class AuthenticationHandler:

protected AuthenticationHandler(IOptionsMonitor<TOptions> options, ILoggerFactory logger, UrlEncoder encoder) 

@dj-nitehawk
Copy link
Author

@paulbreuler thanks mate! updated the snippet to .net 8

@rhyek
Copy link

rhyek commented Jan 5, 2024

The order of UseAuthentication and UseAuthorization is important. It should be:

app
    .UseAuthentication() 
    .UseAuthorization()
    .UseFastEndpoints()
    .UseSwaggerGen();

@dj-nitehawk
Copy link
Author

@rhyek oops sorry! corrected now. cheers mate!

@jhmckimm
Copy link

jhmckimm commented Mar 5, 2024

I don't have the option of calling bld.Services.SwaggerDocument() - what version is this?

I'm using .NET 8.0.102 w/ the following:

<PackageReference Include="FastEndpoints" Version="5.23.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.2"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />

@jhmckimm
Copy link

jhmckimm commented Mar 5, 2024

I don't have the option of calling bld.Services.SwaggerDocument() - what version is this?

I'm using .NET 8.0.102 w/ the following:

<PackageReference Include="FastEndpoints" Version="5.23.0" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.2"/>
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.5.0" />

nvm, I was missing the FastEndpoints.Swagger reference:

<PackageReference Include="FastEndpoints.Swagger" Version="5.23.0" />

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment