Skip to content

Commit

Permalink
fixed AllowAnonymous
Browse files Browse the repository at this point in the history
  • Loading branch information
lkurzyniec committed May 8, 2024
1 parent c3667a1 commit 53da8e3
Show file tree
Hide file tree
Showing 3 changed files with 148 additions and 120 deletions.
@@ -1,61 +1,67 @@
using System.Linq;
using System.Text.RegularExpressions;
using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Configurations;
using HappyCode.NetCoreBoilerplate.Core;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Options;
using Microsoft.FeatureManagement;

namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters
{
public class ApiKeyAuthorizationFilter : IAsyncAuthorizationFilter
{
private static readonly Regex _apiKeyRegex = new Regex(@"^[Aa][Pp][Ii][Kk][Ee][Yy]\s+(?<ApiKey>.+)$", RegexOptions.Compiled);

private readonly IOptions<ApiKeySettings> _options;
private readonly IFeatureManager _featureManager;

public ApiKeyAuthorizationFilter(IOptions<ApiKeySettings> options, IFeatureManager featureManager)
{
_options = options;
_featureManager = featureManager;
}

public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (!(await _featureManager.IsEnabledAsync(FeatureFlags.ApiKey)))
{
return;
}

if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out var values))
{
context.Result = new UnauthorizedObjectResult("Authorization header is missing");
return;
}

var authorization = values.FirstOrDefault();
if (string.IsNullOrWhiteSpace(authorization))
{
context.Result = new UnauthorizedObjectResult("Authorization header is empty");
return;
}

var match = _apiKeyRegex.Match(authorization);
if (!match.Success)
{
context.Result = new UnauthorizedObjectResult("ApiKey Authorization header value not match `ApiKey xxx-xxx`");
return;
}

var apiKeyValue = match.Groups["ApiKey"].Value;
// you can look into DB as well
if (_options.Value.SecretKey != apiKeyValue)
{
context.Result = new UnauthorizedObjectResult("ApiKey Unauthorized");
return;
}
}
}
}
using System.Linq;
using System.Text.RegularExpressions;
using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Configurations;
using HappyCode.NetCoreBoilerplate.Core;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Filters;
using Microsoft.Extensions.Options;
using Microsoft.FeatureManagement;

namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters
{
public class ApiKeyAuthorizationFilter : IAsyncAuthorizationFilter
{
private static readonly Regex _apiKeyRegex = new Regex(@"^[Aa][Pp][Ii][Kk][Ee][Yy]\s+(?<ApiKey>.+)$", RegexOptions.Compiled);

private readonly IOptions<ApiKeySettings> _options;
private readonly IFeatureManager _featureManager;

public ApiKeyAuthorizationFilter(IOptions<ApiKeySettings> options, IFeatureManager featureManager)
{
_options = options;
_featureManager = featureManager;
}

public async Task OnAuthorizationAsync(AuthorizationFilterContext context)
{
if (!(await _featureManager.IsEnabledAsync(FeatureFlags.ApiKey)))
{
return;
}
bool hasAllowAnonymous = context.ActionDescriptor.EndpointMetadata.OfType<IAllowAnonymous>().Any();
if (hasAllowAnonymous)
{
return;
}

if (!context.HttpContext.Request.Headers.TryGetValue("Authorization", out var values))
{
context.Result = new UnauthorizedObjectResult("Authorization header is missing");
return;
}

var authorization = values.FirstOrDefault();
if (string.IsNullOrWhiteSpace(authorization))
{
context.Result = new UnauthorizedObjectResult("Authorization header is empty");
return;
}

var match = _apiKeyRegex.Match(authorization);
if (!match.Success)
{
context.Result = new UnauthorizedObjectResult("ApiKey Authorization header value not match `ApiKey xxx-xxx`");
return;
}

var apiKeyValue = match.Groups["ApiKey"].Value;
// you can look into DB as well
if (_options.Value.SecretKey != apiKeyValue)
{
context.Result = new UnauthorizedObjectResult("ApiKey Unauthorized");
return;
}
}
}
}
@@ -0,0 +1,37 @@
using Microsoft.OpenApi.Models;
using System.Linq;
using Swashbuckle.AspNetCore.SwaggerGen;
using Microsoft.AspNetCore.Authorization;

namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters
{
public class SecurityRequirementSwaggerOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var hasAllowAnonymous = context.ApiDescription.CustomAttributes().OfType<IAllowAnonymous>().Any();
if (hasAllowAnonymous)
{
return;
}

operation.Security.Add(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Name = "ApiKey",
Type = SecuritySchemeType.ApiKey,
In = ParameterLocation.Header,
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "ApiKey",
},
},
Array.Empty<string>()
}
});
}
}
}
@@ -1,59 +1,44 @@
using System.IO;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Registrations
{
public static class SwaggerRegistration
{
public static void AddSwagger(this IServiceCollection services, IConfiguration configuration)
{
string secretKey = configuration.GetValue<string>("ApiKey:SecretKey");

services.AddSwaggerGen(swaggerOptions =>
{
swaggerOptions.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Simple Api",
Version = "v1",
Description = $"ApiKey {secretKey}",
Contact = new OpenApiContact
{
Name = "Łukasz Kurzyniec",
Url = new Uri("https://kurzyniec.pl/"),
}
});
swaggerOptions.OrderActionsBy(x => x.RelativePath);
swaggerOptions.IncludeXmlComments(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "HappyCode.NetCoreBoilerplate.Api.xml"));
swaggerOptions.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme
{
Description = "ApiKey needed to access the endpoints (eg: `Authorization: ApiKey xxx-xxx`)",
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
});
swaggerOptions.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Name = "ApiKey",
Type = SecuritySchemeType.ApiKey,
In = ParameterLocation.Header,
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "ApiKey",
},
},
Array.Empty<string>()
}
});
});
}
}
}
using System.IO;
using HappyCode.NetCoreBoilerplate.Api.Infrastructure.Filters;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

namespace HappyCode.NetCoreBoilerplate.Api.Infrastructure.Registrations
{
public static class SwaggerRegistration
{
public static void AddSwagger(this IServiceCollection services, IConfiguration configuration)
{
string secretKey = configuration.GetValue<string>("ApiKey:SecretKey");

services.AddSwaggerGen(swaggerOptions =>
{
swaggerOptions.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Simple Api",
Version = "v1",
Description = $"ApiKey {secretKey}",
Contact = new OpenApiContact
{
Name = "Łukasz Kurzyniec",
Url = new Uri("https://kurzyniec.pl/"),
}
});
swaggerOptions.OrderActionsBy(x => x.RelativePath);
swaggerOptions.IncludeXmlComments(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "HappyCode.NetCoreBoilerplate.Api.xml"));
swaggerOptions.AddSecurityDefinition("ApiKey", new OpenApiSecurityScheme
{
Description = "ApiKey needed to access the endpoints (eg: `Authorization: ApiKey xxx-xxx`)",
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.ApiKey,
});
swaggerOptions.OperationFilter<SecurityRequirementSwaggerOperationFilter>();
});
}
}
}

0 comments on commit 53da8e3

Please sign in to comment.