diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index b550e8c..322f075 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -35,7 +35,7 @@ jobs: - name: Setup .NET Core SDK uses: actions/setup-dotnet@v1.9.0 with: - dotnet-version: 8.x.x + dotnet-version: 9.x.x # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL diff --git a/LICENSE b/LICENSE index cf26481..7414cdd 100644 --- a/LICENSE +++ b/LICENSE @@ -1,6 +1,6 @@ MIT License -Copyright (c) 2024 Mihir Dilip +Copyright (c) 2025 Mihir Dilip Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal diff --git a/README.md b/README.md index a0698f4..18efedd 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Easy to use and very light weight Microsoft style API Key Authentication Impleme ## .NET (Core) Frameworks Supported .NET Framework 4.6.1 and/or NetStandard 2.0 onwards -Multi targeted: net8.0; net7.0; net6.0; net5.0; netcoreapp3.1; netcoreapp3.0; netstandard2.0; net461 +Multi targeted: net9.0; net8.0; net7.0; net6.0; net5.0; netcoreapp3.1; netcoreapp3.0; netstandard2.0; net461
@@ -384,6 +384,7 @@ public void ConfigureServices(IServiceCollection services) ## Release Notes | Version |           Notes | |---------|-------| +|9.0.0 | | |8.0.1 | | |8.0.0 | | |7.0.0 | | diff --git a/samples/SampleWebApi.Shared/Services/ApiKeyProvider.cs b/samples/SampleWebApi.Shared/Services/ApiKeyProvider.cs index bc4f06d..21df338 100644 --- a/samples/SampleWebApi.Shared/Services/ApiKeyProvider.cs +++ b/samples/SampleWebApi.Shared/Services/ApiKeyProvider.cs @@ -16,7 +16,7 @@ public ApiKeyProvider(ILogger logger, IApiKeyRepository apiKeyR _apiKeyRepository = apiKeyRepository; } - public async Task ProvideAsync(string key) + public async Task ProvideAsync(string key) { try { diff --git a/samples/SampleWebApi_2_0/SampleWebApi_2_0.csproj b/samples/SampleWebApi_2_0/SampleWebApi_2_0.csproj index 8ac49e3..91e3ec9 100644 --- a/samples/SampleWebApi_2_0/SampleWebApi_2_0.csproj +++ b/samples/SampleWebApi_2_0/SampleWebApi_2_0.csproj @@ -4,6 +4,8 @@ netcoreapp2.0 false false + latest + enable diff --git a/samples/SampleWebApi_2_2/SampleWebApi_2_2.csproj b/samples/SampleWebApi_2_2/SampleWebApi_2_2.csproj index 0dafbeb..70329b3 100644 --- a/samples/SampleWebApi_2_2/SampleWebApi_2_2.csproj +++ b/samples/SampleWebApi_2_2/SampleWebApi_2_2.csproj @@ -5,6 +5,8 @@ InProcess false false + latest + enable diff --git a/samples/SampleWebApi_3_1/SampleWebApi_3_1.csproj b/samples/SampleWebApi_3_1/SampleWebApi_3_1.csproj index 5fc5145..4334354 100644 --- a/samples/SampleWebApi_3_1/SampleWebApi_3_1.csproj +++ b/samples/SampleWebApi_3_1/SampleWebApi_3_1.csproj @@ -2,6 +2,7 @@ netcoreapp3.1 + enable false diff --git a/samples/SampleWebApi_5_0/SampleWebApi_5_0.csproj b/samples/SampleWebApi_5_0/SampleWebApi_5_0.csproj index 451e988..0aabe64 100644 --- a/samples/SampleWebApi_5_0/SampleWebApi_5_0.csproj +++ b/samples/SampleWebApi_5_0/SampleWebApi_5_0.csproj @@ -2,6 +2,7 @@ net5.0 + enable false diff --git a/samples/SampleWebApi_7_0/SampleWebApi_7_0.csproj b/samples/SampleWebApi_7_0/SampleWebApi_7_0.csproj index 77f760b..f1992e9 100644 --- a/samples/SampleWebApi_7_0/SampleWebApi_7_0.csproj +++ b/samples/SampleWebApi_7_0/SampleWebApi_7_0.csproj @@ -4,6 +4,7 @@ net7.0 enable enable + false diff --git a/samples/SampleWebApi_9_0/Controllers/ValuesController.cs b/samples/SampleWebApi_9_0/Controllers/ValuesController.cs new file mode 100644 index 0000000..cd46903 --- /dev/null +++ b/samples/SampleWebApi_9_0/Controllers/ValuesController.cs @@ -0,0 +1,36 @@ +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Mvc; +using System.Text; + +namespace SampleWebApi_9_0.Controllers +{ + [Route("api/[controller]")] + [ApiController] + public class ValuesController : ControllerBase + { + // GET api/values + [AllowAnonymous] + [HttpGet] + public ActionResult> Get() + { + return new string[] { "value1", "value2" }; + } + + [HttpGet("claims")] + public ActionResult Claims() + { + var sb = new StringBuilder(); + foreach (var claim in User.Claims) + { + sb.AppendLine($"{claim.Type}: {claim.Value}"); + } + return sb.ToString(); + } + + [HttpGet("forbid")] + public new IActionResult Forbid() + { + return base.Forbid(); + } + } +} diff --git a/samples/SampleWebApi_9_0/Program.cs b/samples/SampleWebApi_9_0/Program.cs new file mode 100644 index 0000000..56f9ce1 --- /dev/null +++ b/samples/SampleWebApi_9_0/Program.cs @@ -0,0 +1,163 @@ +using AspNetCore.Authentication.ApiKey; +using Microsoft.AspNetCore.Authorization; +using SampleWebApi.Repositories; +using SampleWebApi.Services; + +var builder = WebApplication.CreateBuilder(args); + +// Add User repository to the dependency container. +builder.Services.AddTransient(); + +// Add the ApiKey scheme authentication here.. +// It requires Realm to be set in the options if SuppressWWWAuthenticateHeader is not set. +// If an implementation of IApiKeyProvider interface is registered in the dependency register as well as OnValidateKey delegete on options.Events is also set then this delegate will be used instead of an implementation of IApiKeyProvider. +builder.Services.AddAuthentication(ApiKeyDefaults.AuthenticationScheme) + + // The below AddApiKeyInHeaderOrQueryParams without type parameter will require OnValidateKey delegete on options.Events to be set unless an implementation of IApiKeyProvider interface is registered in the dependency register. + // Please note if OnValidateKey delegete on options.Events is also set then this delegate will be used instead of ApiKeyProvider.* + //.AddApiKeyInHeaderOrQueryParams(options => + + // The below AddApiKeyInHeaderOrQueryParams with type parameter will add the ApiKeyProvider to the dependency register. + // Please note if OnValidateKey delegete on options.Events is also set then this delegate will be used instead of ApiKeyProvider. + .AddApiKeyInHeaderOrQueryParams(options => + { + options.Realm = "Sample Web API"; + options.KeyName = "X-API-KEY"; + + //// Optional option to suppress the browser login dialog for ajax calls. + //options.SuppressWWWAuthenticateHeader = true; + + //// Optional option to ignore extra check of ApiKey string after it is validated. + //options.ForLegacyIgnoreExtraValidatedApiKeyCheck = true; + + //// Optional option to ignore authentication if AllowAnonumous metadata/filter attribute is added to an endpoint. + //options.IgnoreAuthenticationIfAllowAnonymous = true; + + //// Optional events to override the ApiKey original logic with custom logic. + //// Only use this if you know what you are doing at your own risk. Any of the events can be assigned. + options.Events = new ApiKeyEvents + { + + //// A delegate assigned to this property will be invoked just before validating the api key. + //OnValidateKey = async (context) => + //{ + // // custom code to handle the api key, create principal and call Success method on context. + // var apiKeyRepository = context.HttpContext.RequestServices.GetRequiredService(); + // var apiKey = await apiKeyRepository.GetApiKeyAsync(context.ApiKey); + // var isValid = apiKey != null && apiKey.Key.Equals(context.ApiKey, StringComparison.OrdinalIgnoreCase); + // if (isValid) + // { + // context.Response.Headers.Add("ValidationCustomHeader", "From OnValidateKey"); + // var claims = new[] + // { + // new Claim(ClaimTypes.NameIdentifier, apiKey.OwnerName, ClaimValueTypes.String, context.Options.ClaimsIssuer), + // new Claim(ClaimTypes.Name, apiKey.OwnerName, ClaimValueTypes.String, context.Options.ClaimsIssuer), + // new Claim("CustomClaimType", "Custom Claim Value - from OnValidateKey") + // }; + // context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name)); + // context.Success(); + // } + // else + // { + // context.NoResult(); + // } + //}, + + //// A delegate assigned to this property will be invoked just before validating the api key. + //// NOTE: Same as above delegate but slightly different implementation which will give same result. + //OnValidateKey = async (context) => + //{ + // // custom code to handle the api key, create principal and call Success method on context. + // var apiKeyRepository = context.HttpContext.RequestServices.GetRequiredService(); + // var apiKey = await apiKeyRepository.GetApiKeyAsync(context.ApiKey); + // var isValid = apiKey != null && apiKey.Key.Equals(context.ApiKey, StringComparison.OrdinalIgnoreCase); + // if (isValid) + // { + // context.Response.Headers.Add("ValidationCustomHeader", "From OnValidateKey"); + // var claims = new[] + // { + // new Claim("CustomClaimType", "Custom Claim Value - from OnValidateKey") + // }; + // context.ValidationSucceeded(apiKey.OwnerName, claims); // claims are optional + // } + // else + // { + // context.ValidationFailed(); + // } + //}, + + //// A delegate assigned to this property will be invoked before a challenge is sent back to the caller when handling unauthorized response. + //OnHandleChallenge = async (context) => + //{ + // // custom code to handle authentication challenge unauthorized response. + // context.Response.StatusCode = StatusCodes.Status401Unauthorized; + // context.Response.Headers.Add("ChallengeCustomHeader", "From OnHandleChallenge"); + // await context.Response.WriteAsync("{\"CustomBody\":\"From OnHandleChallenge\"}"); + // context.Handled(); // important! do not forget to call this method at the end. + //}, + + //// A delegate assigned to this property will be invoked if Authorization fails and results in a Forbidden response. + //OnHandleForbidden = async (context) => + //{ + // // custom code to handle forbidden response. + // context.Response.StatusCode = StatusCodes.Status403Forbidden; + // context.Response.Headers.Add("ForbidCustomHeader", "From OnHandleForbidden"); + // await context.Response.WriteAsync("{\"CustomBody\":\"From OnHandleForbidden\"}"); + // context.Handled(); // important! do not forget to call this method at the end. + //}, + + //// A delegate assigned to this property will be invoked when the authentication succeeds. It will not be called if OnValidateKey delegate is assigned. + //// It can be used for adding claims, headers, etc to the response. + //OnAuthenticationSucceeded = (context) => + //{ + // //custom code to add extra bits to the success response. + // context.Response.Headers.Add("SuccessCustomHeader", "From OnAuthenticationSucceeded"); + // var customClaims = new List + // { + // new Claim("CustomClaimType", "Custom Claim Value - from OnAuthenticationSucceeded") + // }; + // context.AddClaims(customClaims); + // //or can add like this - context.Principal.AddIdentity(new ClaimsIdentity(customClaims)); + // return Task.CompletedTask; + //}, + + //// A delegate assigned to this property will be invoked when the authentication fails. + //OnAuthenticationFailed = (context) => + //{ + // // custom code to handle failed authentication. + // context.Fail("Failed to authenticate"); + // return Task.CompletedTask; + //} + + }; + }); + +builder.Services.AddControllers(options => +{ + // ALWAYS USE HTTPS (SSL) protocol in production when using ApiKey authentication. + //options.Filters.Add(); + +}); //.AddXmlSerializerFormatters() // To enable XML along with JSON; + +// All the requests will need to be authorized. +// Alternatively, add [Authorize] attribute to Controller or Action Method where necessary. +builder.Services.AddAuthorizationBuilder() + .AddFallbackPolicy( + "FallbackPolicy", + new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build() + ); + +var app = builder.Build(); + +// Configure the HTTP request pipeline. + +app.UseHttpsRedirection(); + +app.UseAuthentication(); // NOTE: DEFAULT TEMPLATE DOES NOT HAVE THIS, THIS LINE IS REQUIRED AND HAS TO BE ADDED!!! + +app.UseAuthorization(); + +app.MapControllers(); + +app.Run(); + diff --git a/samples/SampleWebApi_9_0/Properties/launchSettings.json b/samples/SampleWebApi_9_0/Properties/launchSettings.json new file mode 100644 index 0000000..18b1392 --- /dev/null +++ b/samples/SampleWebApi_9_0/Properties/launchSettings.json @@ -0,0 +1,41 @@ +{ + "$schema": "http://json.schemastore.org/launchsettings.json", + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:3920", + "sslPort": 44304 + } + }, + "profiles": { + "http": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "api/values", + "applicationUrl": "http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "api/values", + "applicationUrl": "https://localhost:5001;http://localhost:5000", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "api/values", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/SampleWebApi_9_0/SampleWebApi_9_0.csproj b/samples/SampleWebApi_9_0/SampleWebApi_9_0.csproj new file mode 100644 index 0000000..b08ff33 --- /dev/null +++ b/samples/SampleWebApi_9_0/SampleWebApi_9_0.csproj @@ -0,0 +1,19 @@ + + + + net9.0 + enable + enable + + + + + + + + + + + diff --git a/samples/SampleWebApi_9_0/appsettings.Development.json b/samples/SampleWebApi_9_0/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/samples/SampleWebApi_9_0/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/SampleWebApi_9_0/appsettings.json b/samples/SampleWebApi_9_0/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/samples/SampleWebApi_9_0/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/samples/SampleWebApi_AOT/Program.cs b/samples/SampleWebApi_AOT/Program.cs new file mode 100644 index 0000000..55892f6 --- /dev/null +++ b/samples/SampleWebApi_AOT/Program.cs @@ -0,0 +1,187 @@ +using AspNetCore.Authentication.ApiKey; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using SampleWebApi.Repositories; +using SampleWebApi.Services; +using System.Text; +using System.Text.Json.Serialization; + +var builder = WebApplication.CreateSlimBuilder(args); +builder.WebHost.UseKestrelHttpsConfiguration(); + +// Add User repository to the dependency container. +builder.Services.AddTransient(); + +// Add the ApiKey scheme authentication here.. +// It requires Realm to be set in the options if SuppressWWWAuthenticateHeader is not set. +// If an implementation of IApiKeyProvider interface is registered in the dependency register as well as OnValidateKey delegete on options.Events is also set then this delegate will be used instead of an implementation of IApiKeyProvider. +builder.Services.AddAuthentication(ApiKeyDefaults.AuthenticationScheme) + + // The below AddApiKeyInHeaderOrQueryParams without type parameter will require OnValidateKey delegete on options.Events to be set unless an implementation of IApiKeyProvider interface is registered in the dependency register. + // Please note if OnValidateKey delegete on options.Events is also set then this delegate will be used instead of ApiKeyProvider.* + //.AddApiKeyInHeaderOrQueryParams(options => + + // The below AddApiKeyInHeaderOrQueryParams with type parameter will add the ApiKeyProvider to the dependency register. + // Please note if OnValidateKey delegete on options.Events is also set then this delegate will be used instead of ApiKeyProvider. + .AddApiKeyInHeaderOrQueryParams(options => + { + options.Realm = "Sample Web API"; + options.KeyName = "X-API-KEY"; + + //// Optional option to suppress the browser login dialog for ajax calls. + //options.SuppressWWWAuthenticateHeader = true; + + //// Optional option to ignore extra check of ApiKey string after it is validated. + //options.ForLegacyIgnoreExtraValidatedApiKeyCheck = true; + + //// Optional option to ignore authentication if AllowAnonumous metadata/filter attribute is added to an endpoint. + //options.IgnoreAuthenticationIfAllowAnonymous = true; + + //// Optional events to override the ApiKey original logic with custom logic. + //// Only use this if you know what you are doing at your own risk. Any of the events can be assigned. + options.Events = new ApiKeyEvents + { + + //// A delegate assigned to this property will be invoked just before validating the api key. + //OnValidateKey = async (context) => + //{ + // // custom code to handle the api key, create principal and call Success method on context. + // var apiKeyRepository = context.HttpContext.RequestServices.GetRequiredService(); + // var apiKey = await apiKeyRepository.GetApiKeyAsync(context.ApiKey); + // var isValid = apiKey != null && apiKey.Key.Equals(context.ApiKey, StringComparison.OrdinalIgnoreCase); + // if (isValid) + // { + // context.Response.Headers.Add("ValidationCustomHeader", "From OnValidateKey"); + // var claims = new[] + // { + // new Claim(ClaimTypes.NameIdentifier, apiKey.OwnerName, ClaimValueTypes.String, context.Options.ClaimsIssuer), + // new Claim(ClaimTypes.Name, apiKey.OwnerName, ClaimValueTypes.String, context.Options.ClaimsIssuer), + // new Claim("CustomClaimType", "Custom Claim Value - from OnValidateKey") + // }; + // context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name)); + // context.Success(); + // } + // else + // { + // context.NoResult(); + // } + //}, + + //// A delegate assigned to this property will be invoked just before validating the api key. + //// NOTE: Same as above delegate but slightly different implementation which will give same result. + //OnValidateKey = async (context) => + //{ + // // custom code to handle the api key, create principal and call Success method on context. + // var apiKeyRepository = context.HttpContext.RequestServices.GetRequiredService(); + // var apiKey = await apiKeyRepository.GetApiKeyAsync(context.ApiKey); + // var isValid = apiKey != null && apiKey.Key.Equals(context.ApiKey, StringComparison.OrdinalIgnoreCase); + // if (isValid) + // { + // context.Response.Headers.Add("ValidationCustomHeader", "From OnValidateKey"); + // var claims = new[] + // { + // new Claim("CustomClaimType", "Custom Claim Value - from OnValidateKey") + // }; + // context.ValidationSucceeded(apiKey.OwnerName, claims); // claims are optional + // } + // else + // { + // context.ValidationFailed(); + // } + //}, + + //// A delegate assigned to this property will be invoked before a challenge is sent back to the caller when handling unauthorized response. + //OnHandleChallenge = async (context) => + //{ + // // custom code to handle authentication challenge unauthorized response. + // context.Response.StatusCode = StatusCodes.Status401Unauthorized; + // context.Response.Headers.Add("ChallengeCustomHeader", "From OnHandleChallenge"); + // await context.Response.WriteAsync("{\"CustomBody\":\"From OnHandleChallenge\"}"); + // context.Handled(); // important! do not forget to call this method at the end. + //}, + + //// A delegate assigned to this property will be invoked if Authorization fails and results in a Forbidden response. + //OnHandleForbidden = async (context) => + //{ + // // custom code to handle forbidden response. + // context.Response.StatusCode = StatusCodes.Status403Forbidden; + // context.Response.Headers.Add("ForbidCustomHeader", "From OnHandleForbidden"); + // await context.Response.WriteAsync("{\"CustomBody\":\"From OnHandleForbidden\"}"); + // context.Handled(); // important! do not forget to call this method at the end. + //}, + + //// A delegate assigned to this property will be invoked when the authentication succeeds. It will not be called if OnValidateKey delegate is assigned. + //// It can be used for adding claims, headers, etc to the response. + //OnAuthenticationSucceeded = (context) => + //{ + // //custom code to add extra bits to the success response. + // context.Response.Headers.Add("SuccessCustomHeader", "From OnAuthenticationSucceeded"); + // var customClaims = new List + // { + // new Claim("CustomClaimType", "Custom Claim Value - from OnAuthenticationSucceeded") + // }; + // context.AddClaims(customClaims); + // //or can add like this - context.Principal.AddIdentity(new ClaimsIdentity(customClaims)); + // return Task.CompletedTask; + //}, + + //// A delegate assigned to this property will be invoked when the authentication fails. + //OnAuthenticationFailed = (context) => + //{ + // // custom code to handle failed authentication. + // context.Fail("Failed to authenticate"); + // return Task.CompletedTask; + //} + + }; + }); + +// All the requests will need to be authorized. +// Alternatively, add [Authorize] attribute to Controller or Action Method where necessary. +builder.Services.AddAuthorizationBuilder() + .AddFallbackPolicy( + "FallbackPolicy", + new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build() + ); + +builder.Services.ConfigureHttpJsonOptions(options => +{ + options.SerializerOptions.TypeInfoResolverChain.Insert(0, AppJsonSerializerContext.Default); +}); + +var app = builder.Build(); + +app.UseHttpsRedirection(); + +app.UseAuthentication(); // NOTE: DEFAULT TEMPLATE DOES NOT HAVE THIS, THIS LINE IS REQUIRED AND HAS TO BE ADDED!!! + +app.UseAuthorization(); + +var valuesApi = app.MapGroup("/api/values") + .RequireAuthorization(); +valuesApi.MapGet("/", () => new[] { "value1", "value2" }) + .AllowAnonymous(); +valuesApi.MapGet("/claims", async (context) => +{ + var sb = new StringBuilder(); + foreach (var claim in context.User.Claims) + { + sb.AppendLine($"{claim.Type}: {claim.Value}"); + } + context.Response.StatusCode = 200; + await context.Response.WriteAsync(sb.ToString()); +}); +valuesApi.MapGet("/forbid", async (context) => +{ + await context.ForbidAsync(); +}); + +app.Run(); + + +[JsonSerializable(typeof(string))] +[JsonSerializable(typeof(string[]))] +internal partial class AppJsonSerializerContext : JsonSerializerContext +{ + +} \ No newline at end of file diff --git a/samples/SampleWebApi_AOT/Properties/launchSettings.json b/samples/SampleWebApi_AOT/Properties/launchSettings.json new file mode 100644 index 0000000..b26d850 --- /dev/null +++ b/samples/SampleWebApi_AOT/Properties/launchSettings.json @@ -0,0 +1,17 @@ +{ + "$schema": "https://json.schemastore.org/launchsettings.json", + "profiles": { + "https": { + "commandName": "Project", + "dotnetRunMessages": true, + "launchBrowser": true, + "launchUrl": "api/values", + "applicationUrl": "https://localhost:44304;http://localhost:3920", + "sslPort": 44304, + "useSSL": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} diff --git a/samples/SampleWebApi_AOT/SampleWebApi_AOT.csproj b/samples/SampleWebApi_AOT/SampleWebApi_AOT.csproj new file mode 100644 index 0000000..1695af2 --- /dev/null +++ b/samples/SampleWebApi_AOT/SampleWebApi_AOT.csproj @@ -0,0 +1,21 @@ + + + + net9.0 + enable + enable + true + true + + + + + + + + + + + diff --git a/samples/SampleWebApi_AOT/appsettings.Development.json b/samples/SampleWebApi_AOT/appsettings.Development.json new file mode 100644 index 0000000..0c208ae --- /dev/null +++ b/samples/SampleWebApi_AOT/appsettings.Development.json @@ -0,0 +1,8 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + } +} diff --git a/samples/SampleWebApi_AOT/appsettings.json b/samples/SampleWebApi_AOT/appsettings.json new file mode 100644 index 0000000..10f68b8 --- /dev/null +++ b/samples/SampleWebApi_AOT/appsettings.json @@ -0,0 +1,9 @@ +{ + "Logging": { + "LogLevel": { + "Default": "Information", + "Microsoft.AspNetCore": "Warning" + } + }, + "AllowedHosts": "*" +} diff --git a/src/AspNetCore.Authentication.ApiKey.sln b/src/AspNetCore.Authentication.ApiKey.sln index 1ed81b5..5d1c2be 100644 --- a/src/AspNetCore.Authentication.ApiKey.sln +++ b/src/AspNetCore.Authentication.ApiKey.sln @@ -38,6 +38,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleWebApi_7_0", "..\samp EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleWebApi_8_0", "..\samples\SampleWebApi_8_0\SampleWebApi_8_0.csproj", "{5F0968BA-062C-4C7F-B40C-B1E1641FCEF6}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleWebApi_9_0", "..\samples\SampleWebApi_9_0\SampleWebApi_9_0.csproj", "{B1469317-6D92-45B4-9AE1-FC9B70D26B9B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleWebApi_AOT", "..\samples\SampleWebApi_AOT\SampleWebApi_AOT.csproj", "{AB621904-C1BF-43F8-AFC3-B5097443F8C4}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -80,6 +84,14 @@ Global {5F0968BA-062C-4C7F-B40C-B1E1641FCEF6}.Debug|Any CPU.Build.0 = Debug|Any CPU {5F0968BA-062C-4C7F-B40C-B1E1641FCEF6}.Release|Any CPU.ActiveCfg = Release|Any CPU {5F0968BA-062C-4C7F-B40C-B1E1641FCEF6}.Release|Any CPU.Build.0 = Release|Any CPU + {B1469317-6D92-45B4-9AE1-FC9B70D26B9B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1469317-6D92-45B4-9AE1-FC9B70D26B9B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1469317-6D92-45B4-9AE1-FC9B70D26B9B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1469317-6D92-45B4-9AE1-FC9B70D26B9B}.Release|Any CPU.Build.0 = Release|Any CPU + {AB621904-C1BF-43F8-AFC3-B5097443F8C4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB621904-C1BF-43F8-AFC3-B5097443F8C4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB621904-C1BF-43F8-AFC3-B5097443F8C4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB621904-C1BF-43F8-AFC3-B5097443F8C4}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -94,6 +106,8 @@ Global {D1056CEF-550A-4EEB-B12B-1093DD3125AC} = {3C777BBB-7464-43FB-A046-EA465791AB0C} {BE461B80-9EB5-41AF-9C18-39740A7BD057} = {3C777BBB-7464-43FB-A046-EA465791AB0C} {5F0968BA-062C-4C7F-B40C-B1E1641FCEF6} = {3C777BBB-7464-43FB-A046-EA465791AB0C} + {B1469317-6D92-45B4-9AE1-FC9B70D26B9B} = {3C777BBB-7464-43FB-A046-EA465791AB0C} + {AB621904-C1BF-43F8-AFC3-B5097443F8C4} = {3C777BBB-7464-43FB-A046-EA465791AB0C} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {70815049-1680-480A-BF5A-00536D6C9C20} @@ -101,6 +115,8 @@ Global GlobalSection(SharedMSBuildProjectFiles) = preSolution ..\samples\SampleWebApi.Shared\SampleWebApi.Shared.projitems*{1e1e202b-efb2-40fd-8271-659f36084916}*SharedItemsImports = 5 ..\samples\SampleWebApi.Shared\SampleWebApi.Shared.projitems*{5f0968ba-062c-4c7f-b40c-b1e1641fcef6}*SharedItemsImports = 5 + ..\samples\SampleWebApi.Shared\SampleWebApi.Shared.projitems*{ab621904-c1bf-43f8-afc3-b5097443f8c4}*SharedItemsImports = 5 + ..\samples\SampleWebApi.Shared\SampleWebApi.Shared.projitems*{b1469317-6d92-45b4-9ae1-fc9b70d26b9b}*SharedItemsImports = 5 ..\samples\SampleWebApi.Shared\SampleWebApi.Shared.projitems*{be461b80-9eb5-41af-9c18-39740a7bd057}*SharedItemsImports = 5 ..\samples\SampleWebApi.Shared\SampleWebApi.Shared.projitems*{cabeeeae-3974-4cc4-97f1-18c8d2188daf}*SharedItemsImports = 5 ..\samples\SampleWebApi.Shared\SampleWebApi.Shared.projitems*{d1056cef-550a-4eeb-b12b-1093dd3125ac}*SharedItemsImports = 5 diff --git a/src/AspNetCore.Authentication.ApiKey/ApiKeyExtensions.cs b/src/AspNetCore.Authentication.ApiKey/ApiKeyExtensions.cs index b7269e0..937ddae 100644 --- a/src/AspNetCore.Authentication.ApiKey/ApiKeyExtensions.cs +++ b/src/AspNetCore.Authentication.ApiKey/ApiKeyExtensions.cs @@ -5,7 +5,7 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; using Microsoft.Extensions.Options; -using System; +using System.Diagnostics.CodeAnalysis; namespace AspNetCore.Authentication.ApiKey { @@ -42,7 +42,7 @@ public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder /// /// The configure options. /// The instance of - public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder builder, Action configureOptions) + public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder builder, Action? configureOptions) => builder.AddApiKeyInHeader(ApiKeyDefaults.AuthenticationScheme, configureOptions); /// @@ -53,7 +53,7 @@ public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder /// The authentication scheme. /// The configure options. /// The instance of - public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) + public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) => builder.AddApiKeyInHeader(authenticationScheme, displayName: null, configureOptions: configureOptions); /// @@ -65,7 +65,7 @@ public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder /// The display name. /// The configure options. /// The instance of - public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) + public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) => builder.AddApiKey(authenticationScheme, displayName, configureOptions); @@ -79,8 +79,13 @@ public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder /// /// /// The instance of +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInHeader<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInHeader(ApiKeyDefaults.AuthenticationScheme); +#else public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder builder) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInHeader(ApiKeyDefaults.AuthenticationScheme); +#endif /// /// Adds API Key - In Header authentication scheme to the project. It takes a implementation of as type parameter. @@ -90,8 +95,13 @@ public static AuthenticationBuilder AddApiKeyInHeader(this Auth /// /// The authentication scheme. /// The instance of +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInHeader<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInHeader(authenticationScheme, configureOptions: null); +#else public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder builder, string authenticationScheme) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInHeader(authenticationScheme, configureOptions: null); +#endif /// /// Adds API Key - In Header authentication scheme to the project. It takes a implementation of as type parameter. @@ -101,8 +111,13 @@ public static AuthenticationBuilder AddApiKeyInHeader(this Auth /// /// The . /// The instance of - public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder builder, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInHeader<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInHeader(ApiKeyDefaults.AuthenticationScheme, configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder builder, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInHeader(ApiKeyDefaults.AuthenticationScheme, configureOptions); +#endif /// /// Adds API Key - In Header authentication scheme to the project. It takes a implementation of as type parameter. @@ -113,8 +128,13 @@ public static AuthenticationBuilder AddApiKeyInHeader(this Auth /// The authentication scheme. /// The . /// The instance of - public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInHeader<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInHeader(authenticationScheme, displayName: null, configureOptions: configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInHeader(authenticationScheme, displayName: null, configureOptions: configureOptions); +#endif /// /// Adds API Key - In Header authentication scheme to the project. It takes a implementation of as type parameter. @@ -126,10 +146,15 @@ public static AuthenticationBuilder AddApiKeyInHeader(this Auth /// The display name. /// The . /// The instance of - public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInHeader<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKey(authenticationScheme, displayName, configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInHeader(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKey(authenticationScheme, displayName, configureOptions); +#endif - #endregion // API Key - In Header +#endregion // API Key - In Header #region API Key - In Authorization Header @@ -159,7 +184,7 @@ public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this Authenti /// /// The configure options. /// The instance of - public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this AuthenticationBuilder builder, Action configureOptions) + public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this AuthenticationBuilder builder, Action? configureOptions) => builder.AddApiKeyInAuthorizationHeader(ApiKeyDefaults.AuthenticationScheme, configureOptions); /// @@ -170,7 +195,7 @@ public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this Authenti /// The authentication scheme. /// The configure options. /// The instance of - public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) + public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) => builder.AddApiKeyInAuthorizationHeader(authenticationScheme, displayName: null, configureOptions: configureOptions); /// @@ -182,7 +207,7 @@ public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this Authenti /// The display name. /// The configure options. /// The instance of - public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) + public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) => builder.AddApiKey(authenticationScheme, displayName, configureOptions); @@ -196,8 +221,13 @@ public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this Authenti /// /// /// The instance of +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInAuthorizationHeader<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInAuthorizationHeader(ApiKeyDefaults.AuthenticationScheme); +#else public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this AuthenticationBuilder builder) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInAuthorizationHeader(ApiKeyDefaults.AuthenticationScheme); +#endif /// /// Adds API Key - In Authorization Header authentication scheme to the project. It takes a implementation of as type parameter. @@ -207,8 +237,13 @@ public static AuthenticationBuilder AddApiKeyInAuthorizationHeader /// The authentication scheme. /// The instance of +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInAuthorizationHeader<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInAuthorizationHeader(authenticationScheme, configureOptions: null); +#else public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this AuthenticationBuilder builder, string authenticationScheme) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInAuthorizationHeader(authenticationScheme, configureOptions: null); +#endif /// /// Adds API Key - In Authorization Header authentication scheme to the project. It takes a implementation of as type parameter. @@ -218,8 +253,13 @@ public static AuthenticationBuilder AddApiKeyInAuthorizationHeader /// The . /// The instance of - public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this AuthenticationBuilder builder, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInAuthorizationHeader<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInAuthorizationHeader(ApiKeyDefaults.AuthenticationScheme, configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this AuthenticationBuilder builder, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInAuthorizationHeader(ApiKeyDefaults.AuthenticationScheme, configureOptions); +#endif /// /// Adds API Key - In Authorization Header authentication scheme to the project. It takes a implementation of as type parameter. @@ -230,8 +270,13 @@ public static AuthenticationBuilder AddApiKeyInAuthorizationHeaderThe authentication scheme. /// The . /// The instance of - public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInAuthorizationHeader<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInAuthorizationHeader(authenticationScheme, displayName: null, configureOptions: configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInAuthorizationHeader(authenticationScheme, displayName: null, configureOptions: configureOptions); +#endif /// /// Adds API Key - In Authorization Header authentication scheme to the project. It takes a implementation of as type parameter. @@ -243,8 +288,13 @@ public static AuthenticationBuilder AddApiKeyInAuthorizationHeaderThe display name. /// The . /// The instance of - public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInAuthorizationHeader<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKey(authenticationScheme, displayName, configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInAuthorizationHeader(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKey(authenticationScheme, displayName, configureOptions); +#endif #endregion // API Key - In Authorization Header @@ -276,7 +326,7 @@ public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBu /// /// The configure options. /// The instance of - public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBuilder builder, Action configureOptions) + public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBuilder builder, Action? configureOptions) => builder.AddApiKeyInQueryParams(ApiKeyDefaults.AuthenticationScheme, configureOptions); /// @@ -287,7 +337,7 @@ public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBu /// The authentication scheme. /// The configure options. /// The instance of - public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) + public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) => builder.AddApiKeyInQueryParams(authenticationScheme, displayName: null, configureOptions: configureOptions); /// @@ -299,7 +349,7 @@ public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBu /// The display name. /// The configure options. /// The instance of - public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) + public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) => builder.AddApiKey(authenticationScheme, displayName, configureOptions); @@ -313,8 +363,13 @@ public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBu /// /// /// The instance of +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInQueryParams<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInQueryParams(ApiKeyDefaults.AuthenticationScheme); +#else public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBuilder builder) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInQueryParams(ApiKeyDefaults.AuthenticationScheme); +#endif /// /// Adds API Key - In Query Parameters authentication scheme to the project. It takes a implementation of as type parameter. @@ -324,8 +379,13 @@ public static AuthenticationBuilder AddApiKeyInQueryParams(this /// /// The authentication scheme. /// The instance of +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInQueryParams<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInQueryParams(authenticationScheme, configureOptions: null); +#else public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBuilder builder, string authenticationScheme) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInQueryParams(authenticationScheme, configureOptions: null); +#endif /// /// Adds API Key - In Query Parameters authentication scheme to the project. It takes a implementation of as type parameter. @@ -335,8 +395,13 @@ public static AuthenticationBuilder AddApiKeyInQueryParams(this /// /// The . /// The instance of - public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBuilder builder, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInQueryParams<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInQueryParams(ApiKeyDefaults.AuthenticationScheme, configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBuilder builder, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInQueryParams(ApiKeyDefaults.AuthenticationScheme, configureOptions); +#endif /// /// Adds API Key - In Query Parameters authentication scheme to the project. It takes a implementation of as type parameter. @@ -347,8 +412,13 @@ public static AuthenticationBuilder AddApiKeyInQueryParams(this /// The authentication scheme. /// The . /// The instance of - public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInQueryParams<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInQueryParams(authenticationScheme, displayName: null, configureOptions: configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInQueryParams(authenticationScheme, displayName: null, configureOptions: configureOptions); +#endif /// /// Adds API Key - In Query Parameters authentication scheme to the project. It takes a implementation of as type parameter. @@ -360,20 +430,25 @@ public static AuthenticationBuilder AddApiKeyInQueryParams(this /// The display name. /// The . /// The instance of - public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInQueryParams<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKey(authenticationScheme, displayName, configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInQueryParams(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKey(authenticationScheme, displayName, configureOptions); +#endif - #endregion // API Key - In Query Parameters + #endregion // API Key - In Query Parameters - #region API Key - In Header Or Query Parameters + #region API Key - In Header Or Query Parameters - /// - /// Adds API Key - In Header Or Query Parameters authentication scheme to the project. - /// delegate must be set on the . - /// - /// - /// The instance of - public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder) + /// + /// Adds API Key - In Header Or Query Parameters authentication scheme to the project. + /// delegate must be set on the . + /// + /// + /// The instance of + public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder) => builder.AddApiKeyInHeaderOrQueryParams(ApiKeyDefaults.AuthenticationScheme); /// @@ -393,7 +468,7 @@ public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this Authenti /// /// The configure options. /// The instance of - public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder, Action configureOptions) + public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder, Action? configureOptions) => builder.AddApiKeyInHeaderOrQueryParams(ApiKeyDefaults.AuthenticationScheme, configureOptions); /// @@ -404,7 +479,7 @@ public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this Authenti /// The authentication scheme. /// The configure options. /// The instance of - public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) + public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) => builder.AddApiKeyInHeaderOrQueryParams(authenticationScheme, displayName: null, configureOptions: configureOptions); /// @@ -416,7 +491,7 @@ public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this Authenti /// The display name. /// The configure options. /// The instance of - public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) + public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) => builder.AddApiKey(authenticationScheme, displayName, configureOptions); @@ -430,8 +505,13 @@ public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this Authenti /// /// /// The instance of +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInHeaderOrQueryParams(ApiKeyDefaults.AuthenticationScheme); +#else public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInHeaderOrQueryParams(ApiKeyDefaults.AuthenticationScheme); +#endif /// /// Adds API Key - In Header Or Query Parameters authentication scheme to the project. It takes a implementation of as type parameter. @@ -441,8 +521,13 @@ public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams /// The authentication scheme. /// The instance of +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInHeaderOrQueryParams(authenticationScheme, configureOptions: null); +#else public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder, string authenticationScheme) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInHeaderOrQueryParams(authenticationScheme, configureOptions: null); +#endif /// /// Adds API Key - In Header Or Query Parameters authentication scheme to the project. It takes a implementation of as type parameter. @@ -452,8 +537,13 @@ public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams /// The . /// The instance of - public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInHeaderOrQueryParams(ApiKeyDefaults.AuthenticationScheme, configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInHeaderOrQueryParams(ApiKeyDefaults.AuthenticationScheme, configureOptions); +#endif /// /// Adds API Key - In Header Or Query Parameters authentication scheme to the project. It takes a implementation of as type parameter. @@ -464,8 +554,13 @@ public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParamsThe authentication scheme. /// The . /// The instance of - public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKeyInHeaderOrQueryParams(authenticationScheme, displayName: null, configureOptions: configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInHeaderOrQueryParams(authenticationScheme, displayName: null, configureOptions: configureOptions); +#endif /// /// Adds API Key - In Header Or Query Parameters authentication scheme to the project. It takes a implementation of as type parameter. @@ -477,131 +572,167 @@ public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParamsThe display name. /// The . /// The instance of - public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider => builder.AddApiKey(authenticationScheme, displayName, configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInHeaderOrQueryParams(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKey(authenticationScheme, displayName, configureOptions); +#endif - #endregion // API Key - In Header Or Query Parameters + #endregion // API Key - In Header Or Query Parameters - #region API Key - In Route Values + #region API Key - In Route Values #if NETCOREAPP3_0_OR_GREATER - /// - /// Adds API Key - In Route Values authentication scheme to the project. - /// delegate must be set on the . - /// - /// - /// The instance of - public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder) - => builder.AddApiKeyInRouteValues(ApiKeyDefaults.AuthenticationScheme); - - /// - /// Adds API Key - In Route Values authentication scheme to the project. - /// delegate must be set on the . - /// - /// - /// The authentication scheme. - /// The instance of - public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, string authenticationScheme) - => builder.AddApiKeyInRouteValues(authenticationScheme, configureOptions: null); - - /// - /// Adds API Key - In Route Values authentication scheme to the project. - /// delegate must be set on the Events property on . - /// - /// - /// The configure options. - /// The instance of - public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, Action configureOptions) - => builder.AddApiKeyInRouteValues(ApiKeyDefaults.AuthenticationScheme, configureOptions); - - /// - /// Adds API Key - In Route Values authentication scheme to the project. - /// delegate must be set on the Events property on . - /// - /// - /// The authentication scheme. - /// The configure options. - /// The instance of - public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) - => builder.AddApiKeyInRouteValues(authenticationScheme, displayName: null, configureOptions: configureOptions); - - /// - /// Adds API Key - In Route Values authentication scheme to the project. - /// delegate must be set on the Events property on . - /// - /// - /// The authentication scheme. - /// The display name. - /// The configure options. - /// The instance of - public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) - => builder.AddApiKey(authenticationScheme, displayName, configureOptions); - - - - - - /// - /// Adds API Key - In Route Values authentication scheme to the project. It takes a implementation of as type parameter. - /// If delegate is set on the then it will be used instead of implementation of . - /// - /// - /// - /// The instance of - public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder) where TApiKeyProvider : class, IApiKeyProvider - => builder.AddApiKeyInRouteValues(ApiKeyDefaults.AuthenticationScheme); - - /// - /// Adds API Key - In Route Values authentication scheme to the project. It takes a implementation of as type parameter. - /// If delegate is set on the then it will be used instead of implementation of . - /// - /// - /// - /// The authentication scheme. - /// The instance of - public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, string authenticationScheme) where TApiKeyProvider : class, IApiKeyProvider - => builder.AddApiKeyInRouteValues(authenticationScheme, configureOptions: null); - - /// - /// Adds API Key - In Route Values authentication scheme to the project. It takes a implementation of as type parameter. - /// If delegate is set on the Events property on then it will be used instead of implementation of . - /// - /// - /// - /// The . - /// The instance of - public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider - => builder.AddApiKeyInRouteValues(ApiKeyDefaults.AuthenticationScheme, configureOptions); - - /// - /// Adds API Key - In Route Values authentication scheme to the project. It takes a implementation of as type parameter. - /// If delegate is set on the Events property on then it will be used instead of implementation of . - /// - /// - /// - /// The authentication scheme. - /// The . - /// The instance of - public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider - => builder.AddApiKeyInRouteValues(authenticationScheme, displayName: null, configureOptions: configureOptions); - - /// - /// Adds API Key - In Route Values authentication scheme to the project. It takes a implementation of as type parameter. - /// If delegate is set on the Events property on then it will be used instead of implementation of . - /// - /// - /// - /// The authentication scheme. - /// The display name. - /// The . - /// The instance of - public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) where TApiKeyProvider : class, IApiKeyProvider - => builder.AddApiKey(authenticationScheme, displayName, configureOptions); + /// + /// Adds API Key - In Route Values authentication scheme to the project. + /// delegate must be set on the . + /// + /// + /// The instance of + public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder) + => builder.AddApiKeyInRouteValues(ApiKeyDefaults.AuthenticationScheme); + + /// + /// Adds API Key - In Route Values authentication scheme to the project. + /// delegate must be set on the . + /// + /// + /// The authentication scheme. + /// The instance of + public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, string authenticationScheme) + => builder.AddApiKeyInRouteValues(authenticationScheme, configureOptions: null); + + /// + /// Adds API Key - In Route Values authentication scheme to the project. + /// delegate must be set on the Events property on . + /// + /// + /// The configure options. + /// The instance of + public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, Action? configureOptions) + => builder.AddApiKeyInRouteValues(ApiKeyDefaults.AuthenticationScheme, configureOptions); + + /// + /// Adds API Key - In Route Values authentication scheme to the project. + /// delegate must be set on the Events property on . + /// + /// + /// The authentication scheme. + /// The configure options. + /// The instance of + public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) + => builder.AddApiKeyInRouteValues(authenticationScheme, displayName: null, configureOptions: configureOptions); + + /// + /// Adds API Key - In Route Values authentication scheme to the project. + /// delegate must be set on the Events property on . + /// + /// + /// The authentication scheme. + /// The display name. + /// The configure options. + /// The instance of + public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) + => builder.AddApiKey(authenticationScheme, displayName, configureOptions); + + + + + + /// + /// Adds API Key - In Route Values authentication scheme to the project. It takes a implementation of as type parameter. + /// If delegate is set on the then it will be used instead of implementation of . + /// + /// + /// + /// The instance of +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInRouteValues<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInRouteValues(ApiKeyDefaults.AuthenticationScheme); +#else + public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInRouteValues(ApiKeyDefaults.AuthenticationScheme); #endif - #endregion // API Key - In Route Values + /// + /// Adds API Key - In Route Values authentication scheme to the project. It takes a implementation of as type parameter. + /// If delegate is set on the then it will be used instead of implementation of . + /// + /// + /// + /// The authentication scheme. + /// The instance of +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInRouteValues<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInRouteValues(authenticationScheme, configureOptions: null); +#else + public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, string authenticationScheme) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInRouteValues(authenticationScheme, configureOptions: null); +#endif - private static AuthenticationBuilder AddApiKey(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) + /// + /// Adds API Key - In Route Values authentication scheme to the project. It takes a implementation of as type parameter. + /// If delegate is set on the Events property on then it will be used instead of implementation of . + /// + /// + /// + /// The . + /// The instance of +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInRouteValues<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInRouteValues(ApiKeyDefaults.AuthenticationScheme, configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInRouteValues(ApiKeyDefaults.AuthenticationScheme, configureOptions); +#endif + + /// + /// Adds API Key - In Route Values authentication scheme to the project. It takes a implementation of as type parameter. + /// If delegate is set on the Events property on then it will be used instead of implementation of . + /// + /// + /// + /// The authentication scheme. + /// The . + /// The instance of +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInRouteValues<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInRouteValues(authenticationScheme, displayName: null, configureOptions: configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, string authenticationScheme, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKeyInRouteValues(authenticationScheme, displayName: null, configureOptions: configureOptions); +#endif + + /// + /// Adds API Key - In Route Values authentication scheme to the project. It takes a implementation of as type parameter. + /// If delegate is set on the Events property on then it will be used instead of implementation of . + /// + /// + /// + /// The authentication scheme. + /// The display name. + /// The . + /// The instance of +#if NET5_0_OR_GREATER + public static AuthenticationBuilder AddApiKeyInRouteValues<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider>(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKey(authenticationScheme, displayName, configureOptions); +#else + public static AuthenticationBuilder AddApiKeyInRouteValues(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider + => builder.AddApiKey(authenticationScheme, displayName, configureOptions); +#endif + +#endif + #endregion // API Key - In Route Values + + +#if NET5_0_OR_GREATER + private static AuthenticationBuilder AddApiKey<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyHandler>(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) + where TApiKeyHandler : AuthenticationHandler +#else + private static AuthenticationBuilder AddApiKey(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) where TApiKeyHandler : AuthenticationHandler +#endif { // Adds post configure options to the pipeline. builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton, ApiKeyPostConfigureOptions>()); @@ -610,9 +741,15 @@ private static AuthenticationBuilder AddApiKey(this Authenticati return builder.AddScheme(authenticationScheme, displayName, configureOptions); } - private static AuthenticationBuilder AddApiKey(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) +#if NET5_0_OR_GREATER + private static AuthenticationBuilder AddApiKey<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyProvider, [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TApiKeyHandler>(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) + where TApiKeyProvider : class, IApiKeyProvider + where TApiKeyHandler : AuthenticationHandler +#else + private static AuthenticationBuilder AddApiKey(this AuthenticationBuilder builder, string authenticationScheme, string? displayName, Action? configureOptions) where TApiKeyProvider : class, IApiKeyProvider where TApiKeyHandler : AuthenticationHandler +#endif { // Adds implementation of IApiKeyProvider to the dependency container. builder.Services.AddTransient(); diff --git a/src/AspNetCore.Authentication.ApiKey/ApiKeyHandlerBase.cs b/src/AspNetCore.Authentication.ApiKey/ApiKeyHandlerBase.cs index 4d330a8..70714e4 100644 --- a/src/AspNetCore.Authentication.ApiKey/ApiKeyHandlerBase.cs +++ b/src/AspNetCore.Authentication.ApiKey/ApiKeyHandlerBase.cs @@ -7,9 +7,7 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; -using System; using System.Text.Encodings.Web; -using System.Threading.Tasks; namespace AspNetCore.Authentication.ApiKey { @@ -20,24 +18,24 @@ public abstract class ApiKeyHandlerBase : AuthenticationHandler { #if NET8_0_OR_GREATER - protected ApiKeyHandlerBase(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) - : base(options, logger, encoder) - { - } + protected ApiKeyHandlerBase(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) + : base(options, logger, encoder) + { + } - [Obsolete("ISystemClock is obsolete, use TimeProvider on AuthenticationSchemeOptions instead.")] + [Obsolete("ISystemClock is obsolete, use TimeProvider on AuthenticationSchemeOptions instead.")] #endif - protected ApiKeyHandlerBase(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + protected ApiKeyHandlerBase(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } - private string Challenge => $"{GetWwwAuthenticateSchemeName()} realm=\"{Options.Realm}\", charset=\"UTF-8\", in=\"{GetWwwAuthenticateInParameter()}\", key_name=\"{Options.KeyName}\""; + private string Challenge => $"{GetWwwAuthenticateSchemeName()} realm=\"{Options.Realm}\", charset=\"UTF-8\", in=\"{GetWwwAuthenticateInParameter()}\", key_name=\"{Options.KeyName}\""; /// /// Get or set . /// - protected new ApiKeyEvents Events { get => (ApiKeyEvents)base.Events; set => base.Events = value; } + protected new ApiKeyEvents Events { get => (ApiKeyEvents)base.Events!; set => base.Events = value; } /// /// Create an instance of . @@ -147,7 +145,7 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop await base.HandleChallengeAsync(properties); } - private async Task RaiseAndHandleEventValidateKeyAsync(string apiKey) + private async Task RaiseAndHandleEventValidateKeyAsync(string apiKey) { var validateApiContext = new ApiKeyValidateKeyContext(Context, Scheme, Options, apiKey); await Events.ValidateKeyAsync(validateApiContext).ConfigureAwait(false); @@ -185,16 +183,19 @@ private async Task RaiseAndHandleAuthenticationSucceededAsyn { // If claims principal is set and is authenticated then build a ticket by calling and return success. authenticationSucceededContext.Success(); - return authenticationSucceededContext.Result; + if (authenticationSucceededContext.Result != null) + { + return authenticationSucceededContext.Result; + } } Logger.LogError("No authenticated prinicipal set."); return AuthenticateResult.Fail("No authenticated prinicipal set."); } - private async Task ValidateUsingApiKeyProviderAsync(string apiKey) + private async Task ValidateUsingApiKeyProviderAsync(string apiKey) { - IApiKeyProvider apiKeyProvider = null; + IApiKeyProvider? apiKeyProvider = null; if (Options.ApiKeyProviderType != null) { apiKeyProvider = ActivatorUtilities.GetServiceOrCreateInstance(Context.RequestServices, Options.ApiKeyProviderType) as IApiKeyProvider; @@ -211,6 +212,13 @@ private async Task ValidateUsingApiKeyProviderAsync(string apiKey) } finally { +#if NETCOREAPP3_0_OR_GREATER + if (apiKeyProvider is IAsyncDisposable asyncDisposableApiKeyProvider) + { + await asyncDisposableApiKeyProvider.DisposeAsync().ConfigureAwait(false); + } +#endif + if (apiKeyProvider is IDisposable disposableApiKeyProvider) { disposableApiKeyProvider.Dispose(); @@ -219,11 +227,11 @@ private async Task ValidateUsingApiKeyProviderAsync(string apiKey) } private string GetWwwAuthenticateSchemeName() - { + { return Options.ForLegacyUseKeyNameAsSchemeNameOnWWWAuthenticateHeader ? Options.KeyName : Scheme.Name; - } + } protected abstract string GetWwwAuthenticateInParameter(); diff --git a/src/AspNetCore.Authentication.ApiKey/ApiKeyInAuthorizationHeaderHandler.cs b/src/AspNetCore.Authentication.ApiKey/ApiKeyInAuthorizationHeaderHandler.cs index e4a6ea9..5a5cf9f 100644 --- a/src/AspNetCore.Authentication.ApiKey/ApiKeyInAuthorizationHeaderHandler.cs +++ b/src/AspNetCore.Authentication.ApiKey/ApiKeyInAuthorizationHeaderHandler.cs @@ -6,27 +6,25 @@ using Microsoft.Extensions.Options; using Microsoft.Extensions.Primitives; using Microsoft.Net.Http.Headers; -using System; using System.Net.Http.Headers; using System.Text.Encodings.Web; -using System.Threading.Tasks; namespace AspNetCore.Authentication.ApiKey { - public class ApiKeyInAuthorizationHeaderHandler : ApiKeyHandlerBase + public class ApiKeyInAuthorizationHeaderHandler : ApiKeyHandlerBase { - private const string WwwAuthenticateInParameter = "authorization_header"; - protected override string GetWwwAuthenticateInParameter() => WwwAuthenticateInParameter; + private const string WwwAuthenticateInParameter = "authorization_header"; + protected override string GetWwwAuthenticateInParameter() => WwwAuthenticateInParameter; #if NET8_0_OR_GREATER - protected ApiKeyInAuthorizationHeaderHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) - : base(options, logger, encoder) - { - } + protected ApiKeyInAuthorizationHeaderHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) + : base(options, logger, encoder) + { + } - [Obsolete("ISystemClock is obsolete, use TimeProvider on AuthenticationSchemeOptions instead.")] + [Obsolete("ISystemClock is obsolete, use TimeProvider on AuthenticationSchemeOptions instead.")] #endif - public ApiKeyInAuthorizationHeaderHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + public ApiKeyInAuthorizationHeaderHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } @@ -40,11 +38,11 @@ protected override Task ParseApiKeyAsync() ) ) { - return Task.FromResult(headerValue.Parameter); + return Task.FromResult(headerValue.Parameter ?? string.Empty); } // No ApiKey found return Task.FromResult(string.Empty); } - } + } } \ No newline at end of file diff --git a/src/AspNetCore.Authentication.ApiKey/ApiKeyInHeaderHandler.cs b/src/AspNetCore.Authentication.ApiKey/ApiKeyInHeaderHandler.cs index bc5c807..cf5c32e 100644 --- a/src/AspNetCore.Authentication.ApiKey/ApiKeyInHeaderHandler.cs +++ b/src/AspNetCore.Authentication.ApiKey/ApiKeyInHeaderHandler.cs @@ -4,27 +4,24 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using System; -using System.Linq; using System.Text.Encodings.Web; -using System.Threading.Tasks; namespace AspNetCore.Authentication.ApiKey { - public class ApiKeyInHeaderHandler : ApiKeyHandlerBase + public class ApiKeyInHeaderHandler : ApiKeyHandlerBase { - private const string WwwAuthenticateInParameter = "header"; - protected override string GetWwwAuthenticateInParameter() => WwwAuthenticateInParameter; + private const string WwwAuthenticateInParameter = "header"; + protected override string GetWwwAuthenticateInParameter() => WwwAuthenticateInParameter; #if NET8_0_OR_GREATER - protected ApiKeyInHeaderHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) - : base(options, logger, encoder) - { - } + protected ApiKeyInHeaderHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) + : base(options, logger, encoder) + { + } - [Obsolete("ISystemClock is obsolete, use TimeProvider on AuthenticationSchemeOptions instead.")] + [Obsolete("ISystemClock is obsolete, use TimeProvider on AuthenticationSchemeOptions instead.")] #endif - public ApiKeyInHeaderHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + public ApiKeyInHeaderHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } @@ -33,7 +30,7 @@ protected override Task ParseApiKeyAsync() { if (Request.Headers.TryGetValue(Options.KeyName, out var value)) { - return Task.FromResult(value.FirstOrDefault()); + return Task.FromResult(value.FirstOrDefault() ?? string.Empty); } // No ApiKey found diff --git a/src/AspNetCore.Authentication.ApiKey/ApiKeyInHeaderOrQueryParamsHandler.cs b/src/AspNetCore.Authentication.ApiKey/ApiKeyInHeaderOrQueryParamsHandler.cs index 715a02d..9ea2b71 100644 --- a/src/AspNetCore.Authentication.ApiKey/ApiKeyInHeaderOrQueryParamsHandler.cs +++ b/src/AspNetCore.Authentication.ApiKey/ApiKeyInHeaderOrQueryParamsHandler.cs @@ -5,28 +5,25 @@ using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Microsoft.Net.Http.Headers; -using System; -using System.Linq; using System.Net.Http.Headers; using System.Text.Encodings.Web; -using System.Threading.Tasks; namespace AspNetCore.Authentication.ApiKey { public class ApiKeyInHeaderOrQueryParamsHandler : ApiKeyHandlerBase { - private const string WwwAuthenticateInParameter = "header_or_query_params"; - protected override string GetWwwAuthenticateInParameter() => WwwAuthenticateInParameter; + private const string WwwAuthenticateInParameter = "header_or_query_params"; + protected override string GetWwwAuthenticateInParameter() => WwwAuthenticateInParameter; #if NET8_0_OR_GREATER - protected ApiKeyInHeaderOrQueryParamsHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) - : base(options, logger, encoder) - { - } + protected ApiKeyInHeaderOrQueryParamsHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) + : base(options, logger, encoder) + { + } - [Obsolete("ISystemClock is obsolete, use TimeProvider on AuthenticationSchemeOptions instead.")] + [Obsolete("ISystemClock is obsolete, use TimeProvider on AuthenticationSchemeOptions instead.")] #endif - public ApiKeyInHeaderOrQueryParamsHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + public ApiKeyInHeaderOrQueryParamsHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } @@ -36,13 +33,13 @@ protected override Task ParseApiKeyAsync() // Try query parameter if (Request.Query.TryGetValue(Options.KeyName, out var value)) { - return Task.FromResult(value.FirstOrDefault()); + return Task.FromResult(value.FirstOrDefault() ?? string.Empty); } // No ApiKey query parameter found try headers if (Request.Headers.TryGetValue(Options.KeyName, out var headerValue)) { - return Task.FromResult(headerValue.FirstOrDefault()); + return Task.FromResult(headerValue.FirstOrDefault() ?? string.Empty); } // No ApiKey query parameter or header found then try Authorization header @@ -51,7 +48,7 @@ protected override Task ParseApiKeyAsync() && authHeaderValue.Scheme.Equals(Options.KeyName, StringComparison.OrdinalIgnoreCase) ) { - return Task.FromResult(authHeaderValue.Parameter); + return Task.FromResult(authHeaderValue.Parameter ?? string.Empty); } // No ApiKey found diff --git a/src/AspNetCore.Authentication.ApiKey/ApiKeyInQueryParamsHandler.cs b/src/AspNetCore.Authentication.ApiKey/ApiKeyInQueryParamsHandler.cs index 64456e8..a2e7de4 100644 --- a/src/AspNetCore.Authentication.ApiKey/ApiKeyInQueryParamsHandler.cs +++ b/src/AspNetCore.Authentication.ApiKey/ApiKeyInQueryParamsHandler.cs @@ -4,27 +4,24 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using System; -using System.Linq; using System.Text.Encodings.Web; -using System.Threading.Tasks; namespace AspNetCore.Authentication.ApiKey { public class ApiKeyInQueryParamsHandler : ApiKeyHandlerBase { - private const string WwwAuthenticateInParameter = "query_params"; - protected override string GetWwwAuthenticateInParameter() => WwwAuthenticateInParameter; + private const string WwwAuthenticateInParameter = "query_params"; + protected override string GetWwwAuthenticateInParameter() => WwwAuthenticateInParameter; #if NET8_0_OR_GREATER - protected ApiKeyInQueryParamsHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) - : base(options, logger, encoder) - { - } + protected ApiKeyInQueryParamsHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) + : base(options, logger, encoder) + { + } - [Obsolete("ISystemClock is obsolete, use TimeProvider on AuthenticationSchemeOptions instead.")] + [Obsolete("ISystemClock is obsolete, use TimeProvider on AuthenticationSchemeOptions instead.")] #endif - public ApiKeyInQueryParamsHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + public ApiKeyInQueryParamsHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } @@ -33,7 +30,7 @@ protected override Task ParseApiKeyAsync() { if (Request.Query.TryGetValue(Options.KeyName, out var value)) { - return Task.FromResult(value.FirstOrDefault()); + return Task.FromResult(value.FirstOrDefault() ?? string.Empty); } // No ApiKey query parameter found diff --git a/src/AspNetCore.Authentication.ApiKey/ApiKeyInRouteValuesHandler.cs b/src/AspNetCore.Authentication.ApiKey/ApiKeyInRouteValuesHandler.cs index d5efa53..e4e3e8f 100644 --- a/src/AspNetCore.Authentication.ApiKey/ApiKeyInRouteValuesHandler.cs +++ b/src/AspNetCore.Authentication.ApiKey/ApiKeyInRouteValuesHandler.cs @@ -5,26 +5,24 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using System; using System.Text.Encodings.Web; -using System.Threading.Tasks; namespace AspNetCore.Authentication.ApiKey { - public class ApiKeyInRouteValuesHandler : ApiKeyHandlerBase + public class ApiKeyInRouteValuesHandler : ApiKeyHandlerBase { - private const string WwwAuthenticateInParameter = "route_values"; - protected override string GetWwwAuthenticateInParameter() => WwwAuthenticateInParameter; + private const string WwwAuthenticateInParameter = "route_values"; + protected override string GetWwwAuthenticateInParameter() => WwwAuthenticateInParameter; #if NET8_0_OR_GREATER - protected ApiKeyInRouteValuesHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) - : base(options, logger, encoder) - { - } + protected ApiKeyInRouteValuesHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder) + : base(options, logger, encoder) + { + } - [Obsolete("ISystemClock is obsolete, use TimeProvider on AuthenticationSchemeOptions instead.")] + [Obsolete("ISystemClock is obsolete, use TimeProvider on AuthenticationSchemeOptions instead.")] #endif - public ApiKeyInRouteValuesHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + public ApiKeyInRouteValuesHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } @@ -33,7 +31,7 @@ protected override Task ParseApiKeyAsync() { if (Request.RouteValues.TryGetValue(Options.KeyName, out var value) && value != null && value.GetType() == typeof(string)) { - return Task.FromResult(value.ToString()); + return Task.FromResult(value.ToString() ?? string.Empty); } // No ApiKey query parameter found diff --git a/src/AspNetCore.Authentication.ApiKey/ApiKeyOptions.cs b/src/AspNetCore.Authentication.ApiKey/ApiKeyOptions.cs index b862348..2fb2b5c 100644 --- a/src/AspNetCore.Authentication.ApiKey/ApiKeyOptions.cs +++ b/src/AspNetCore.Authentication.ApiKey/ApiKeyOptions.cs @@ -2,7 +2,7 @@ // Licensed under the MIT License. See LICENSE file in the project root for license information. using Microsoft.AspNetCore.Authentication; -using System; +using System.Diagnostics.CodeAnalysis; namespace AspNetCore.Authentication.ApiKey { @@ -19,14 +19,14 @@ public ApiKeyOptions() /// /// This is required property. It is the name of the header or query parameter of the API Key. /// - public string KeyName { get; set; } + public string KeyName { get; set; } = string.Empty; /// /// Gets or sets the realm property. It is used with WWW-Authenticate response header when challenging un-authenticated requests. /// Required to be set if SuppressWWWAuthenticateHeader is not set to true. /// /// - public string Realm { get; set; } + public string? Realm { get; set; } /// /// Default value is false. @@ -44,7 +44,7 @@ public ApiKeyOptions() /// public new ApiKeyEvents Events { - get => (ApiKeyEvents)base.Events; + get => (ApiKeyEvents)base.Events!; set => base.Events = value; } @@ -70,6 +70,9 @@ public ApiKeyOptions() public bool IgnoreAuthenticationIfAllowAnonymous { get; set; } #endif - internal Type ApiKeyProviderType { get; set; } = null; +#if NET5_0_OR_GREATER + [DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] +#endif + internal Type? ApiKeyProviderType { get; set; } = null; } } diff --git a/src/AspNetCore.Authentication.ApiKey/ApiKeyPostConfigureOptions.cs b/src/AspNetCore.Authentication.ApiKey/ApiKeyPostConfigureOptions.cs index 13a0a3d..e4312c9 100644 --- a/src/AspNetCore.Authentication.ApiKey/ApiKeyPostConfigureOptions.cs +++ b/src/AspNetCore.Authentication.ApiKey/ApiKeyPostConfigureOptions.cs @@ -1,7 +1,6 @@ // Copyright (c) Mihir Dilip. All rights reserved. // Licensed under the MIT License. See LICENSE file in the project root for license information. -using System; using Microsoft.Extensions.Options; namespace AspNetCore.Authentication.ApiKey @@ -11,7 +10,7 @@ namespace AspNetCore.Authentication.ApiKey /// class ApiKeyPostConfigureOptions : IPostConfigureOptions { - public void PostConfigure(string name, ApiKeyOptions options) + public void PostConfigure(string? name, ApiKeyOptions options) { if (!options.SuppressWWWAuthenticateHeader && string.IsNullOrWhiteSpace(options.Realm)) { diff --git a/src/AspNetCore.Authentication.ApiKey/ApiKeyUtils.cs b/src/AspNetCore.Authentication.ApiKey/ApiKeyUtils.cs index dc87ba8..1fb7719 100644 --- a/src/AspNetCore.Authentication.ApiKey/ApiKeyUtils.cs +++ b/src/AspNetCore.Authentication.ApiKey/ApiKeyUtils.cs @@ -1,9 +1,6 @@ // Copyright (c) Mihir Dilip. All rights reserved. // Licensed under the MIT License. See LICENSE file in the project root for license information. -using System; -using System.Collections.Generic; -using System.Linq; using System.Security.Claims; namespace AspNetCore.Authentication.ApiKey @@ -23,7 +20,7 @@ internal static class ApiKeyUtils /// The claims issuer. /// The list of claims. /// - internal static ClaimsPrincipal BuildClaimsPrincipal(string ownerName, string schemeName, string claimsIssuer, IEnumerable claims = null) + internal static ClaimsPrincipal BuildClaimsPrincipal(string? ownerName, string schemeName, string? claimsIssuer, IEnumerable? claims = null) { if (string.IsNullOrWhiteSpace(schemeName)) throw new ArgumentNullException(nameof(schemeName)); diff --git a/src/AspNetCore.Authentication.ApiKey/AspNetCore.Authentication.ApiKey.csproj b/src/AspNetCore.Authentication.ApiKey/AspNetCore.Authentication.ApiKey.csproj index b0a7d5a..bea29b3 100644 --- a/src/AspNetCore.Authentication.ApiKey/AspNetCore.Authentication.ApiKey.csproj +++ b/src/AspNetCore.Authentication.ApiKey/AspNetCore.Authentication.ApiKey.csproj @@ -1,23 +1,31 @@  - net8.0;net7.0;net6.0;net5.0;netcoreapp3.1;netcoreapp3.0;netstandard2.0;net461 - 8.0.1 + net9.0;net8.0;net7.0;net6.0;net5.0;netcoreapp3.1;netcoreapp3.0;netstandard2.0;net461 + 9.0.0 https://github.com/mihirdilip/aspnetcore-authentication-apiKey/tree/$(Version) https://github.com/mihirdilip/aspnetcore-authentication-apiKey/tree/$(Version) - aspnetcore, security, authentication, microsoft, microsoft.aspnetcore.authentication, microsoft-aspnetcore-authentication, microsoft.aspnetcore.authentication.apikey, microsoft-aspnetcore-authentication-apikey, asp-net-core, netstandard, netstandard20, apikey-authentication, api-key-authentication, apikeyauthentication, dotnetcore, dotnetcore3.1, net5, net5.0, net6, net6.0, net7, net7.0, net8, net8.0, asp-net-core-apikey-authentication, aspnetcore-apikey-authentication, net5-apikey-authentication, asp-net-core-authentication, aspnetcore-authentication, net5-authentication, asp, aspnet, apikey, api-key, authentication-scheme - - Added support to have ApiKey in request route identified by route pattern key from netcoreapp3.0 onwards -- Readme updated + aspnetcore, security, authentication, microsoft, microsoft.aspnetcore.authentication, microsoft-aspnetcore-authentication, microsoft.aspnetcore.authentication.apikey, microsoft-aspnetcore-authentication-apikey, asp-net-core, netstandard, netstandard20, apikey-authentication, api-key-authentication, apikeyauthentication, dotnetcore, dotnetcore3.1, net5, net5.0, net6, net6.0, net7, net7.0, net8, net8.0, net9, net9.0, asp-net-core-apikey-authentication, aspnetcore-apikey-authentication, net5-apikey-authentication, asp-net-core-authentication, aspnetcore-authentication, net5-authentication, asp, aspnet, apikey, api-key, authentication-scheme + - net9.0 support added +- Sample project for net9.0 added +- Readme updated +- Nullable reference types enabled +- Language version set to latest +- Implicit usings enabled +- AOT support added Easy to use and very light weight Microsoft style API Key Authentication Implementation for ASP.NET Core. It can be setup so that it can accept API Key either in Header, Authorization Header, QueryParams, HeaderOrQueryParams or RouteValues. Mihir Dilip Mihir Dilip - Copyright (c) 2024 Mihir Dilip + Copyright (c) 2025 Mihir Dilip true $(AssemblyName) git - + latest + enable + enable + true true LICENSE README.md @@ -27,10 +35,10 @@ true - + false - + true @@ -49,7 +57,7 @@ - + @@ -67,7 +75,7 @@ - + diff --git a/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyAuthenticationFailedContext.cs b/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyAuthenticationFailedContext.cs index 8224421..40608dd 100644 --- a/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyAuthenticationFailedContext.cs +++ b/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyAuthenticationFailedContext.cs @@ -3,7 +3,6 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; -using System; namespace AspNetCore.Authentication.ApiKey { diff --git a/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyAuthenticationSucceededContext.cs b/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyAuthenticationSucceededContext.cs index f95db1d..3150384 100644 --- a/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyAuthenticationSucceededContext.cs +++ b/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyAuthenticationSucceededContext.cs @@ -3,8 +3,6 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; -using System; -using System.Collections.Generic; using System.Security.Claims; namespace AspNetCore.Authentication.ApiKey @@ -24,13 +22,13 @@ public class ApiKeyAuthenticationSucceededContext : ResultContext public ApiKeyAuthenticationSucceededContext(HttpContext context, AuthenticationScheme scheme, ApiKeyOptions options, ClaimsPrincipal principal) : base(context, scheme, options) { - base.Principal = principal; + Principal = principal; } /// /// Get the containing the user claims. /// - public new ClaimsPrincipal Principal => base.Principal; + public new ClaimsPrincipal? Principal { get => base.Principal; private set => base.Principal = value; } /// /// Called to replace the claims principal. The supplied principal will replace the value of the @@ -40,7 +38,7 @@ public ApiKeyAuthenticationSucceededContext(HttpContext context, AuthenticationS /// public void ReplacePrincipal(ClaimsPrincipal principal) { - base.Principal = principal ?? throw new ArgumentNullException(nameof(principal)); + Principal = principal ?? throw new ArgumentNullException(nameof(principal)); } /// @@ -57,7 +55,7 @@ public void ReplacePrincipal(ClaimsPrincipal principal) public void AddClaim(Claim claim) { if (claim == null) throw new ArgumentNullException(nameof(claim)); - (Principal?.Identity as ClaimsIdentity).AddClaim(claim); + (Principal?.Identity as ClaimsIdentity)?.AddClaim(claim); } /// @@ -68,7 +66,7 @@ public void AddClaim(Claim claim) public void AddClaims(IEnumerable claims) { if (claims == null) throw new ArgumentNullException(nameof(claims)); - (Principal?.Identity as ClaimsIdentity).AddClaims(claims); + (Principal?.Identity as ClaimsIdentity)?.AddClaims(claims); } } } diff --git a/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyEvents.cs b/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyEvents.cs index 5f1940c..b28e549 100644 --- a/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyEvents.cs +++ b/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyEvents.cs @@ -1,9 +1,6 @@ // Copyright (c) Mihir Dilip. All rights reserved. // Licensed under the MIT License. See LICENSE file in the project root for license information. -using System; -using System.Threading.Tasks; - namespace AspNetCore.Authentication.ApiKey { /// @@ -20,7 +17,7 @@ public class ApiKeyEvents /// or construct an authentication principal & attach it to the context.Principal property and finally call context.Success() method. /// If only context.Principal property set without calling context.Success() method then, Success() method is automaticalled called. /// - public Func OnValidateKey { get; set; } + public Func? OnValidateKey { get; set; } /// /// A delegate assigned to this property will be invoked when the authentication succeeds. It will not be called if delegate is assigned. @@ -29,12 +26,12 @@ public class ApiKeyEvents /// /// Only use this if you know what you are doing. /// - public Func OnAuthenticationSucceeded { get; set; } + public Func? OnAuthenticationSucceeded { get; set; } /// /// A delegate assigned to this property will be invoked when the authentication fails. /// - public Func OnAuthenticationFailed { get; set; } + public Func? OnAuthenticationFailed { get; set; } /// /// A delegate assigned to this property will be invoked before a challenge is sent back to the caller when handling unauthorized response. @@ -46,7 +43,7 @@ public class ApiKeyEvents /// changing the 401 result to 302 of a login page or external sign-in location.) /// Call context.Handled() at the end so that any default logic for this challenge will be skipped. /// - public Func OnHandleChallenge { get; set; } + public Func? OnHandleChallenge { get; set; } /// /// A delegate assigned to this property will be invoked if Authorization fails and results in a Forbidden response. @@ -56,7 +53,7 @@ public class ApiKeyEvents /// Set the delegate to handle Forbid. /// Call context.Handled() at the end so that any default logic will be skipped. /// - public Func OnHandleForbidden { get; set; } + public Func? OnHandleForbidden { get; set; } diff --git a/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyValidateKeyContext.cs b/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyValidateKeyContext.cs index 104e048..56cf86d 100644 --- a/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyValidateKeyContext.cs +++ b/src/AspNetCore.Authentication.ApiKey/Events/ApiKeyValidateKeyContext.cs @@ -3,8 +3,6 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; -using System; -using System.Collections.Generic; using System.Security.Claims; namespace AspNetCore.Authentication.ApiKey @@ -38,7 +36,7 @@ public ApiKeyValidateKeyContext(HttpContext context, AuthenticationScheme scheme /// and method will also be called. /// /// Claims to be added to the identity. - public void ValidationSucceeded(IEnumerable claims = null) + public void ValidationSucceeded(IEnumerable? claims = null) { ValidationSucceeded(null, claims); } @@ -50,7 +48,7 @@ public void ValidationSucceeded(IEnumerable claims = null) /// /// The owner name to be added to claims as and if not already added with . /// Claims to be added to the identity. - public void ValidationSucceeded(string ownerName, IEnumerable claims = null) + public void ValidationSucceeded(string? ownerName, IEnumerable? claims = null) { Principal = ApiKeyUtils.BuildClaimsPrincipal(ownerName, Scheme.Name, Options.ClaimsIssuer, claims); Success(); @@ -61,7 +59,7 @@ public void ValidationSucceeded(string ownerName, IEnumerable claims = nu /// otherwise, method will be called. /// /// (Optional) The failure message. - public void ValidationFailed(string failureMessage = null) + public void ValidationFailed(string? failureMessage = null) { if (string.IsNullOrWhiteSpace(failureMessage)) { diff --git a/src/AspNetCore.Authentication.ApiKey/GlobalSuppressions.cs b/src/AspNetCore.Authentication.ApiKey/GlobalSuppressions.cs index 323efae..2963b08 100644 --- a/src/AspNetCore.Authentication.ApiKey/GlobalSuppressions.cs +++ b/src/AspNetCore.Authentication.ApiKey/GlobalSuppressions.cs @@ -7,3 +7,5 @@ [assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "", Scope = "member", Target = "~M:AspNetCore.Authentication.ApiKey.ApiKeyAuthenticationSucceededContext.AddClaim(System.Security.Claims.Claim)")] [assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "", Scope = "member", Target = "~M:AspNetCore.Authentication.ApiKey.ApiKeyAuthenticationSucceededContext.AddClaims(System.Collections.Generic.IEnumerable{System.Security.Claims.Claim})")] +[assembly: SuppressMessage("Style", "IDE0130:Namespace does not match folder structure", Justification = "", Scope = "namespace", Target = "~N:AspNetCore.Authentication.ApiKey")] +[assembly: SuppressMessage("Style", "IDE0290:Use primary constructor", Justification = "")] diff --git a/src/AspNetCore.Authentication.ApiKey/IApiKey.cs b/src/AspNetCore.Authentication.ApiKey/IApiKey.cs index 671c208..9b2fc55 100644 --- a/src/AspNetCore.Authentication.ApiKey/IApiKey.cs +++ b/src/AspNetCore.Authentication.ApiKey/IApiKey.cs @@ -1,7 +1,6 @@ // Copyright (c) Mihir Dilip. All rights reserved. // Licensed under the MIT License. See LICENSE file in the project root for license information. -using System.Collections.Generic; using System.Security.Claims; namespace AspNetCore.Authentication.ApiKey diff --git a/src/AspNetCore.Authentication.ApiKey/IApiKeyProvider.cs b/src/AspNetCore.Authentication.ApiKey/IApiKeyProvider.cs index ad05040..586c8e0 100644 --- a/src/AspNetCore.Authentication.ApiKey/IApiKeyProvider.cs +++ b/src/AspNetCore.Authentication.ApiKey/IApiKeyProvider.cs @@ -1,8 +1,6 @@ // Copyright (c) Mihir Dilip. All rights reserved. // Licensed under the MIT License. See LICENSE file in the project root for license information. -using System.Threading.Tasks; - namespace AspNetCore.Authentication.ApiKey { /// @@ -15,6 +13,6 @@ public interface IApiKeyProvider /// /// /// - Task ProvideAsync(string key); + Task ProvideAsync(string key); } } diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyExtensionsTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyExtensionsTests.cs index 0dba8bd..78c9e66 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyExtensionsTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyExtensionsTests.cs @@ -3,9 +3,6 @@ using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.DependencyInjection; -using System; -using System.Linq; -using System.Threading.Tasks; using Xunit; namespace AspNetCore.Authentication.ApiKey.Tests @@ -194,7 +191,7 @@ public void AddApiKeyInHeader_TApiKeyProvider_IApiKeyProvider_is_registered_as_t services.AddAuthentication() .AddApiKeyInHeader(); - var serviceDescriptor = Assert.Single(services.Where(s => s.ServiceType == typeof(IApiKeyProvider))); + var serviceDescriptor = Assert.Single(services, s => s.ServiceType == typeof(IApiKeyProvider)); Assert.Equal(typeof(IApiKeyProvider), serviceDescriptor.ServiceType); Assert.Equal(typeof(MockApiKeyProvider), serviceDescriptor.ImplementationType); Assert.Equal(ServiceLifetime.Transient, serviceDescriptor.Lifetime); @@ -217,12 +214,12 @@ public void AddApiKeyInHeader_TApiKeyProvider_does_not_replace_previously_user_r var serviceDescriptors = services.Where(s => s.ServiceType == typeof(IApiKeyProvider)); Assert.Equal(2, serviceDescriptors.Count()); - var serviceDescriptor = Assert.Single(serviceDescriptors.Where(s => s.ImplementationType == typeof(MockApiKeyProvider))); + var serviceDescriptor = Assert.Single(serviceDescriptors, s => s.ImplementationType == typeof(MockApiKeyProvider)); Assert.Equal(typeof(IApiKeyProvider), serviceDescriptor.ServiceType); Assert.Equal(typeof(MockApiKeyProvider), serviceDescriptor.ImplementationType); Assert.Equal(ServiceLifetime.Transient, serviceDescriptor.Lifetime); - serviceDescriptor = Assert.Single(serviceDescriptors.Where(s => s.ImplementationType == typeof(MockApiKeyProvider2))); + serviceDescriptor = Assert.Single(serviceDescriptors, s => s.ImplementationType == typeof(MockApiKeyProvider2)); Assert.Equal(typeof(IApiKeyProvider), serviceDescriptor.ServiceType); Assert.Equal(typeof(MockApiKeyProvider2), serviceDescriptor.ImplementationType); Assert.Equal(ServiceLifetime.Singleton, serviceDescriptor.Lifetime); @@ -489,7 +486,7 @@ public void AddApiKeyInAuthorizationHeader_TApiKeyProvider_IApiKeyProvider_is_re services.AddAuthentication() .AddApiKeyInAuthorizationHeader(); - var serviceDescriptor = Assert.Single(services.Where(s => s.ServiceType == typeof(IApiKeyProvider))); + var serviceDescriptor = Assert.Single(services, s => s.ServiceType == typeof(IApiKeyProvider)); Assert.Equal(typeof(IApiKeyProvider), serviceDescriptor.ServiceType); Assert.Equal(typeof(MockApiKeyProvider), serviceDescriptor.ImplementationType); Assert.Equal(ServiceLifetime.Transient, serviceDescriptor.Lifetime); @@ -512,12 +509,12 @@ public void AddApiKeyInAuthorizationHeader_TApiKeyProvider_does_not_replace_prev var serviceDescriptors = services.Where(s => s.ServiceType == typeof(IApiKeyProvider)); Assert.Equal(2, serviceDescriptors.Count()); - var serviceDescriptor = Assert.Single(serviceDescriptors.Where(s => s.ImplementationType == typeof(MockApiKeyProvider))); + var serviceDescriptor = Assert.Single(serviceDescriptors, s => s.ImplementationType == typeof(MockApiKeyProvider)); Assert.Equal(typeof(IApiKeyProvider), serviceDescriptor.ServiceType); Assert.Equal(typeof(MockApiKeyProvider), serviceDescriptor.ImplementationType); Assert.Equal(ServiceLifetime.Transient, serviceDescriptor.Lifetime); - serviceDescriptor = Assert.Single(serviceDescriptors.Where(s => s.ImplementationType == typeof(MockApiKeyProvider2))); + serviceDescriptor = Assert.Single(serviceDescriptors, s => s.ImplementationType == typeof(MockApiKeyProvider2)); Assert.Equal(typeof(IApiKeyProvider), serviceDescriptor.ServiceType); Assert.Equal(typeof(MockApiKeyProvider2), serviceDescriptor.ImplementationType); Assert.Equal(ServiceLifetime.Singleton, serviceDescriptor.Lifetime); @@ -784,7 +781,7 @@ public void AddApiKeyInQueryParams_TApiKeyProvider_IApiKeyProvider_is_registered services.AddAuthentication() .AddApiKeyInQueryParams(); - var serviceDescriptor = Assert.Single(services.Where(s => s.ServiceType == typeof(IApiKeyProvider))); + var serviceDescriptor = Assert.Single(services, s => s.ServiceType == typeof(IApiKeyProvider)); Assert.Equal(typeof(IApiKeyProvider), serviceDescriptor.ServiceType); Assert.Equal(typeof(MockApiKeyProvider), serviceDescriptor.ImplementationType); Assert.Equal(ServiceLifetime.Transient, serviceDescriptor.Lifetime); @@ -807,12 +804,12 @@ public void AddApiKeyInQueryParams_TApiKeyProvider_does_not_replace_previously_u var serviceDescriptors = services.Where(s => s.ServiceType == typeof(IApiKeyProvider)); Assert.Equal(2, serviceDescriptors.Count()); - var serviceDescriptor = Assert.Single(serviceDescriptors.Where(s => s.ImplementationType == typeof(MockApiKeyProvider))); + var serviceDescriptor = Assert.Single(serviceDescriptors, s => s.ImplementationType == typeof(MockApiKeyProvider)); Assert.Equal(typeof(IApiKeyProvider), serviceDescriptor.ServiceType); Assert.Equal(typeof(MockApiKeyProvider), serviceDescriptor.ImplementationType); Assert.Equal(ServiceLifetime.Transient, serviceDescriptor.Lifetime); - serviceDescriptor = Assert.Single(serviceDescriptors.Where(s => s.ImplementationType == typeof(MockApiKeyProvider2))); + serviceDescriptor = Assert.Single(serviceDescriptors, s => s.ImplementationType == typeof(MockApiKeyProvider2)); Assert.Equal(typeof(IApiKeyProvider), serviceDescriptor.ServiceType); Assert.Equal(typeof(MockApiKeyProvider2), serviceDescriptor.ImplementationType); Assert.Equal(ServiceLifetime.Singleton, serviceDescriptor.Lifetime); @@ -1079,7 +1076,7 @@ public void AddApiKeyInHeaderOrQueryParams_TApiKeyProvider_IApiKeyProvider_is_re services.AddAuthentication() .AddApiKeyInHeaderOrQueryParams(); - var serviceDescriptor = Assert.Single(services.Where(s => s.ServiceType == typeof(IApiKeyProvider))); + var serviceDescriptor = Assert.Single(services, s => s.ServiceType == typeof(IApiKeyProvider)); Assert.Equal(typeof(IApiKeyProvider), serviceDescriptor.ServiceType); Assert.Equal(typeof(MockApiKeyProvider), serviceDescriptor.ImplementationType); Assert.Equal(ServiceLifetime.Transient, serviceDescriptor.Lifetime); @@ -1102,12 +1099,12 @@ public void AddApiKeyInHeaderOrQueryParams_TApiKeyProvider_does_not_replace_prev var serviceDescriptors = services.Where(s => s.ServiceType == typeof(IApiKeyProvider)); Assert.Equal(2, serviceDescriptors.Count()); - var serviceDescriptor = Assert.Single(serviceDescriptors.Where(s => s.ImplementationType == typeof(MockApiKeyProvider))); + var serviceDescriptor = Assert.Single(serviceDescriptors, s => s.ImplementationType == typeof(MockApiKeyProvider)); Assert.Equal(typeof(IApiKeyProvider), serviceDescriptor.ServiceType); Assert.Equal(typeof(MockApiKeyProvider), serviceDescriptor.ImplementationType); Assert.Equal(ServiceLifetime.Transient, serviceDescriptor.Lifetime); - serviceDescriptor = Assert.Single(serviceDescriptors.Where(s => s.ImplementationType == typeof(MockApiKeyProvider2))); + serviceDescriptor = Assert.Single(serviceDescriptors, s => s.ImplementationType == typeof(MockApiKeyProvider2)); Assert.Equal(typeof(IApiKeyProvider), serviceDescriptor.ServiceType); Assert.Equal(typeof(MockApiKeyProvider2), serviceDescriptor.ImplementationType); Assert.Equal(ServiceLifetime.Singleton, serviceDescriptor.Lifetime); @@ -1203,7 +1200,7 @@ public void AddApiKeyInHeaderOrQueryParams_TApiKeyProvider_allows_chaining_with_ private class MockApiKeyProvider : IApiKeyProvider { - public Task ProvideAsync(string key) + public Task ProvideAsync(string key) { throw new NotImplementedException(); } @@ -1211,7 +1208,7 @@ public Task ProvideAsync(string key) private class MockApiKeyProvider2 : IApiKeyProvider { - public Task ProvideAsync(string key) + public Task ProvideAsync(string key) { throw new NotImplementedException(); } diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyHandlerBaseTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyHandlerBaseTests.cs index 87864c4..e83ff62 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyHandlerBaseTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyHandlerBaseTests.cs @@ -4,21 +4,16 @@ using AspNetCore.Authentication.ApiKey.Tests.Infrastructure; using Microsoft.AspNetCore.Authorization; using Microsoft.Extensions.DependencyInjection; -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; using System.Net.Http; using System.Net.Http.Headers; -using System.Security.Claims; using System.Text.Json; -using System.Threading.Tasks; using Xunit; namespace AspNetCore.Authentication.ApiKey.Tests { - public class ApiKeyHandlerBaseTests - { + public class ApiKeyHandlerBaseTests + { private const string HeaderFromEventsKey = nameof(HeaderFromEventsKey); private const string HeaderFromEventsValue = nameof(HeaderFromEventsValue); @@ -88,7 +83,7 @@ public async Task HandleChallange_using_OnHandleChallenge() options.Events.OnHandleChallenge = context => { context.HttpContext.Response.Headers[HeaderFromEventsKey] = HeaderFromEventsValue; - return Task.CompletedTask; + return Task.CompletedTask; }; }); using var client = server.CreateClient(); @@ -395,126 +390,126 @@ public async Task HandleAuthenticate_OnAuthenticationSucceeded_result_not_null() Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } - #endregion // HandleAuthenticate + #endregion // HandleAuthenticate - #region Multi-Scheme + #region Multi-Scheme - [Fact] - public async Task MultiScheme() - { - var keyName1 = "Key1"; - var keyName2 = "Key2"; - var keyName3 = "Key3"; - var keyName4 = "Key4"; - var claimProvider1 = new ClaimDto { Type = "Provider", Value = "1" }; - var claimProvider2 = new ClaimDto { Type = "Provider", Value = "2" }; - var claimRole = new ClaimDto(FakeApiKeys.FakeRoleClaim); + [Fact] + public async Task MultiScheme() + { + var keyName1 = "Key1"; + var keyName2 = "Key2"; + var keyName3 = "Key3"; + var keyName4 = "Key4"; + var claimProvider1 = new ClaimDto { Type = "Provider", Value = "1" }; + var claimProvider2 = new ClaimDto { Type = "Provider", Value = "2" }; + var claimRole = new ClaimDto(FakeApiKeys.FakeRoleClaim); var schemes = new List { "InHeader", "InHeaderWithProvider", "InAuthorizationHeader", "InQueryParams" }; using var server = TestServerBuilder.BuildTestServer(services => - { - services.AddAuthentication("InHeader") - .AddApiKeyInHeader("InHeader", options => - { - options.Realm = TestServerBuilder.Realm; - options.KeyName = keyName1; - options.Events.OnValidateKey = context => - { - context.Response.Headers["X-Custom"] = "InHeader Scheme"; - context.ValidationSucceeded(); - return Task.CompletedTask; - }; - }) - .AddApiKeyInHeader("InHeaderWithProvider", options => - { - options.Realm = TestServerBuilder.Realm; - options.KeyName = keyName2; - }) - .AddApiKeyInAuthorizationHeader("InAuthorizationHeader", options => - { - options.Realm = TestServerBuilder.Realm; - options.KeyName = keyName3; - }) - .AddApiKeyInQueryParams("InQueryParams", options => - { - options.Realm = TestServerBuilder.Realm; - options.KeyName = keyName4; - }); + { + services.AddAuthentication("InHeader") + .AddApiKeyInHeader("InHeader", options => + { + options.Realm = TestServerBuilder.Realm; + options.KeyName = keyName1; + options.Events.OnValidateKey = context => + { + context.Response.Headers["X-Custom"] = "InHeader Scheme"; + context.ValidationSucceeded(); + return Task.CompletedTask; + }; + }) + .AddApiKeyInHeader("InHeaderWithProvider", options => + { + options.Realm = TestServerBuilder.Realm; + options.KeyName = keyName2; + }) + .AddApiKeyInAuthorizationHeader("InAuthorizationHeader", options => + { + options.Realm = TestServerBuilder.Realm; + options.KeyName = keyName3; + }) + .AddApiKeyInQueryParams("InQueryParams", options => + { + options.Realm = TestServerBuilder.Realm; + options.KeyName = keyName4; + }); #if !(NET461 || NETSTANDARD2_0 || NETCOREAPP2_1) - services.Configure(options => options.FallbackPolicy = new AuthorizationPolicyBuilder(schemes.ToArray()).RequireAuthenticatedUser().Build()); + services.Configure(options => options.FallbackPolicy = new AuthorizationPolicyBuilder([.. schemes]).RequireAuthenticatedUser().Build()); #endif }); - using var client = server.CreateClient(); - - using var request1 = new HttpRequestMessage(HttpMethod.Get, TestServerBuilder.ClaimsPrincipalUrl + "?scheme=" + schemes[0]); - request1.Headers.Add(keyName1, FakeApiKeys.FakeKey); - using var response1 = await client.SendAsync(request1); - Assert.True(response1.IsSuccessStatusCode); - Assert.Equal(HttpStatusCode.OK, response1.StatusCode); - var response1Principal = await DeserializeClaimsPrincipalAsync(response1); - Assert.Contains(response1.Headers, r => r.Key == "X-Custom" && r.Value.Any(v => v == "InHeader Scheme")); - Assert.DoesNotContain(response1Principal.Claims, c => c.Type == claimProvider1.Type && c.Value == claimProvider1.Value); - Assert.DoesNotContain(response1Principal.Claims, c => c.Type == claimProvider2.Type && c.Value == claimProvider2.Value); - Assert.DoesNotContain(response1Principal.Claims, c => c.Type == claimRole.Type && c.Value == claimRole.Value); - - - using var request2 = new HttpRequestMessage(HttpMethod.Get, TestServerBuilder.ClaimsPrincipalUrl + "?scheme=" + schemes[1]); - request2.Headers.Add(keyName2, FakeApiKeys.FakeKey); - using var response2 = await client.SendAsync(request2); - Assert.True(response2.IsSuccessStatusCode); - Assert.Equal(HttpStatusCode.OK, response2.StatusCode); - var response2Principal = await DeserializeClaimsPrincipalAsync(response2); - Assert.DoesNotContain(response2.Headers, r => r.Key == "X-Custom" && r.Value.Any(v => v == "InHeader Scheme")); - Assert.Contains(response2Principal.Claims, c => c.Type == claimProvider1.Type && c.Value == claimProvider1.Value); - Assert.DoesNotContain(response2Principal.Claims, c => c.Type == claimProvider2.Type && c.Value == claimProvider2.Value); - Assert.DoesNotContain(response2Principal.Claims, c => c.Type == claimRole.Type && c.Value == claimRole.Value); - - - using var request3 = new HttpRequestMessage(HttpMethod.Get, TestServerBuilder.ClaimsPrincipalUrl + "?scheme=" + schemes[2]); - request3.Headers.Authorization = new AuthenticationHeaderValue(keyName3, FakeApiKeys.FakeKey); - using var response3 = await client.SendAsync(request3); - Assert.True(response3.IsSuccessStatusCode); - Assert.Equal(HttpStatusCode.OK, response3.StatusCode); - var response3Principal = await DeserializeClaimsPrincipalAsync(response3); - Assert.DoesNotContain(response3.Headers, r => r.Key == "X-Custom" && r.Value.Any(v => v == "InHeader Scheme")); - Assert.DoesNotContain(response3Principal.Claims, c => c.Type == claimProvider1.Type && c.Value == claimProvider1.Value); - Assert.Contains(response3Principal.Claims, c => c.Type == claimProvider2.Type && c.Value == claimProvider2.Value); - Assert.DoesNotContain(response3Principal.Claims, c => c.Type == claimRole.Type && c.Value == claimRole.Value); - - - using var request4 = new HttpRequestMessage(HttpMethod.Get, $"{TestServerBuilder.ClaimsPrincipalUrl}?scheme={schemes[3]}&{keyName4}={FakeApiKeys.FakeKey}"); - using var response4 = await client.SendAsync(request4); - Assert.True(response4.IsSuccessStatusCode); - Assert.Equal(HttpStatusCode.OK, response3.StatusCode); - var response4Principal = await DeserializeClaimsPrincipalAsync(response4); - Assert.DoesNotContain(response4.Headers, r => r.Key == "X-Custom" && r.Value.Any(v => v == "InHeader Scheme")); - Assert.DoesNotContain(response4Principal.Claims, c => c.Type == claimProvider1.Type && c.Value == claimProvider1.Value); - Assert.DoesNotContain(response4Principal.Claims, c => c.Type == claimProvider2.Type && c.Value == claimProvider2.Value); - Assert.Contains(response4Principal.Claims, c => c.Type == claimRole.Type && c.Value == claimRole.Value); - } - - #endregion // Multi-Scheme - - private static async Task DeserializeClaimsPrincipalAsync(HttpResponseMessage response) - { + using var client = server.CreateClient(); + + using var request1 = new HttpRequestMessage(HttpMethod.Get, TestServerBuilder.ClaimsPrincipalUrl + "?scheme=" + schemes[0]); + request1.Headers.Add(keyName1, FakeApiKeys.FakeKey); + using var response1 = await client.SendAsync(request1); + Assert.True(response1.IsSuccessStatusCode); + Assert.Equal(HttpStatusCode.OK, response1.StatusCode); + var response1Principal = await DeserializeClaimsPrincipalAsync(response1); + Assert.Contains(response1.Headers, r => r.Key == "X-Custom" && r.Value.Any(v => v == "InHeader Scheme")); + Assert.DoesNotContain(response1Principal.Claims, c => c.Type == claimProvider1.Type && c.Value == claimProvider1.Value); + Assert.DoesNotContain(response1Principal.Claims, c => c.Type == claimProvider2.Type && c.Value == claimProvider2.Value); + Assert.DoesNotContain(response1Principal.Claims, c => c.Type == claimRole.Type && c.Value == claimRole.Value); + + + using var request2 = new HttpRequestMessage(HttpMethod.Get, TestServerBuilder.ClaimsPrincipalUrl + "?scheme=" + schemes[1]); + request2.Headers.Add(keyName2, FakeApiKeys.FakeKey); + using var response2 = await client.SendAsync(request2); + Assert.True(response2.IsSuccessStatusCode); + Assert.Equal(HttpStatusCode.OK, response2.StatusCode); + var response2Principal = await DeserializeClaimsPrincipalAsync(response2); + Assert.DoesNotContain(response2.Headers, r => r.Key == "X-Custom" && r.Value.Any(v => v == "InHeader Scheme")); + Assert.Contains(response2Principal.Claims, c => c.Type == claimProvider1.Type && c.Value == claimProvider1.Value); + Assert.DoesNotContain(response2Principal.Claims, c => c.Type == claimProvider2.Type && c.Value == claimProvider2.Value); + Assert.DoesNotContain(response2Principal.Claims, c => c.Type == claimRole.Type && c.Value == claimRole.Value); + + + using var request3 = new HttpRequestMessage(HttpMethod.Get, TestServerBuilder.ClaimsPrincipalUrl + "?scheme=" + schemes[2]); + request3.Headers.Authorization = new AuthenticationHeaderValue(keyName3, FakeApiKeys.FakeKey); + using var response3 = await client.SendAsync(request3); + Assert.True(response3.IsSuccessStatusCode); + Assert.Equal(HttpStatusCode.OK, response3.StatusCode); + var response3Principal = await DeserializeClaimsPrincipalAsync(response3); + Assert.DoesNotContain(response3.Headers, r => r.Key == "X-Custom" && r.Value.Any(v => v == "InHeader Scheme")); + Assert.DoesNotContain(response3Principal.Claims, c => c.Type == claimProvider1.Type && c.Value == claimProvider1.Value); + Assert.Contains(response3Principal.Claims, c => c.Type == claimProvider2.Type && c.Value == claimProvider2.Value); + Assert.DoesNotContain(response3Principal.Claims, c => c.Type == claimRole.Type && c.Value == claimRole.Value); + + + using var request4 = new HttpRequestMessage(HttpMethod.Get, $"{TestServerBuilder.ClaimsPrincipalUrl}?scheme={schemes[3]}&{keyName4}={FakeApiKeys.FakeKey}"); + using var response4 = await client.SendAsync(request4); + Assert.True(response4.IsSuccessStatusCode); + Assert.Equal(HttpStatusCode.OK, response3.StatusCode); + var response4Principal = await DeserializeClaimsPrincipalAsync(response4); + Assert.DoesNotContain(response4.Headers, r => r.Key == "X-Custom" && r.Value.Any(v => v == "InHeader Scheme")); + Assert.DoesNotContain(response4Principal.Claims, c => c.Type == claimProvider1.Type && c.Value == claimProvider1.Value); + Assert.DoesNotContain(response4Principal.Claims, c => c.Type == claimProvider2.Type && c.Value == claimProvider2.Value); + Assert.Contains(response4Principal.Claims, c => c.Type == claimRole.Type && c.Value == claimRole.Value); + } + + #endregion // Multi-Scheme + + private static async Task DeserializeClaimsPrincipalAsync(HttpResponseMessage response) + { return JsonSerializer.Deserialize(await response.Content.ReadAsStringAsync()); } - private class FakeApiKeyProviderLocal_1 : IApiKeyProvider - { - public Task ProvideAsync(string key) - { - return Task.FromResult((IApiKey)new FakeApiKey(key, "Test", new List { new("Provider", "1") })); - } - } + private class FakeApiKeyProviderLocal_1 : IApiKeyProvider + { + public Task ProvideAsync(string key) + { + return Task.FromResult((IApiKey?)new FakeApiKey(key, "Test", [new("Provider", "1")])); + } + } private class FakeApiKeyProviderLocal_2 : IApiKeyProvider { - public Task ProvideAsync(string key) + public Task ProvideAsync(string key) { - return Task.FromResult((IApiKey)new FakeApiKey(key, "Test", new List { new("Provider", "2") })); + return Task.FromResult((IApiKey?)new FakeApiKey(key, "Test", [new("Provider", "2")])); } } } diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInAuthorizationHeaderHandlerTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInAuthorizationHeaderHandlerTests.cs index f7bd386..6cc9a6b 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInAuthorizationHeaderHandlerTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInAuthorizationHeaderHandlerTests.cs @@ -6,25 +6,23 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using System; using System.Net; using System.Net.Http; using System.Net.Http.Headers; -using System.Threading.Tasks; using Xunit; namespace AspNetCore.Authentication.ApiKey.Tests { - public class ApiKeyInAuthorizationHeaderHandlerTests : IDisposable - { + public class ApiKeyInAuthorizationHeaderHandlerTests : IDisposable + { private readonly TestServer _server; - private readonly HttpClient _client; - private readonly TestServer _serverWithProvider; - private readonly HttpClient _clientWithProvider; - private bool _disposedValue; + private readonly HttpClient _client; + private readonly TestServer _serverWithProvider; + private readonly HttpClient _clientWithProvider; + private bool _disposedValue; - public ApiKeyInAuthorizationHeaderHandlerTests() - { + public ApiKeyInAuthorizationHeaderHandlerTests() + { _server = TestServerBuilder.BuildInAuthorizationHeaderServer(); _client = _server.CreateClient(); @@ -92,13 +90,13 @@ public async Task Verify_challenge_www_authenticate_header() } [Fact] - public async Task Unauthorized() - { + public async Task Unauthorized() + { using var request = new HttpRequestMessage(HttpMethod.Get, TestServerBuilder.BaseUrl); using var response = await _client.SendAsync(request); Assert.False(response.IsSuccessStatusCode); Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); - } + } [Fact] public async Task Success() @@ -211,39 +209,39 @@ public async Task TApiKeyProvider_invalid_key_unauthotized_with_key_name() Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } - protected virtual void Dispose(bool disposing) - { - if (!_disposedValue) - { - if (disposing) - { - // TODO: dispose managed state (managed objects) - - _client?.Dispose(); - _server?.Dispose(); - - _clientWithProvider?.Dispose(); - _serverWithProvider?.Dispose(); - } - - // TODO: free unmanaged resources (unmanaged objects) and override finalizer - // TODO: set large fields to null - _disposedValue = true; - } - } - - // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources - // ~ApiKeyInAuthorizationHeaderHandlerTests() - // { - // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - // Dispose(disposing: false); - // } - - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + + _client?.Dispose(); + _server?.Dispose(); + + _clientWithProvider?.Dispose(); + _serverWithProvider?.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + _disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~ApiKeyInAuthorizationHeaderHandlerTests() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } } diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInHeaderHandlerTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInHeaderHandlerTests.cs index f1c3f99..a60f118 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInHeaderHandlerTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInHeaderHandlerTests.cs @@ -6,24 +6,22 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using System; using System.Net; using System.Net.Http; -using System.Threading.Tasks; using Xunit; namespace AspNetCore.Authentication.ApiKey.Tests { - public class ApiKeyInHeaderHandlerTests : IDisposable - { + public class ApiKeyInHeaderHandlerTests : IDisposable + { private readonly TestServer _server; - private readonly HttpClient _client; - private readonly TestServer _serverWithProvider; - private readonly HttpClient _clientWithProvider; - private bool _disposedValue; + private readonly HttpClient _client; + private readonly TestServer _serverWithProvider; + private readonly HttpClient _clientWithProvider; + private bool _disposedValue; - public ApiKeyInHeaderHandlerTests() - { + public ApiKeyInHeaderHandlerTests() + { _server = TestServerBuilder.BuildInHeaderServer(); _client = _server.CreateClient(); @@ -91,13 +89,13 @@ public async Task Verify_challenge_www_authenticate_header() } [Fact] - public async Task Unauthorized() - { + public async Task Unauthorized() + { using var request = new HttpRequestMessage(HttpMethod.Get, TestServerBuilder.BaseUrl); using var response = await _client.SendAsync(request); Assert.False(response.IsSuccessStatusCode); Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); - } + } [Fact] public async Task Success() @@ -149,39 +147,39 @@ public async Task TApiKeyProvider_invalid_key_unauthotized() Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } - protected virtual void Dispose(bool disposing) - { - if (!_disposedValue) - { - if (disposing) - { - // TODO: dispose managed state (managed objects) - - _client?.Dispose(); - _server?.Dispose(); - - _clientWithProvider?.Dispose(); - _serverWithProvider?.Dispose(); - } - - // TODO: free unmanaged resources (unmanaged objects) and override finalizer - // TODO: set large fields to null - _disposedValue = true; - } - } - - // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources - // ~ApiKeyInHeaderHandlerTests() - // { - // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - // Dispose(disposing: false); - // } - - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + + _client?.Dispose(); + _server?.Dispose(); + + _clientWithProvider?.Dispose(); + _serverWithProvider?.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + _disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~ApiKeyInHeaderHandlerTests() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } } diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInHeaderOrQueryParamsHandlerTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInHeaderOrQueryParamsHandlerTests.cs index 30cc48d..0388ff9 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInHeaderOrQueryParamsHandlerTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInHeaderOrQueryParamsHandlerTests.cs @@ -6,25 +6,23 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using System; using System.Net; using System.Net.Http; using System.Net.Http.Headers; -using System.Threading.Tasks; using Xunit; namespace AspNetCore.Authentication.ApiKey.Tests { - public class ApiKeyInHeaderOrQueryParamsHandlerTests : IDisposable - { + public class ApiKeyInHeaderOrQueryParamsHandlerTests : IDisposable + { private readonly TestServer _server; - private readonly HttpClient _client; - private readonly TestServer _serverWithProvider; - private readonly HttpClient _clientWithProvider; - private bool _disposedValue; + private readonly HttpClient _client; + private readonly TestServer _serverWithProvider; + private readonly HttpClient _clientWithProvider; + private bool _disposedValue; - public ApiKeyInHeaderOrQueryParamsHandlerTests() - { + public ApiKeyInHeaderOrQueryParamsHandlerTests() + { _server = TestServerBuilder.BuildInHeaderOrQueryParamsServer(); _client = _server.CreateClient(); @@ -43,13 +41,13 @@ public async Task Verify_Handler() Assert.NotNull(scheme); Assert.Equal(typeof(ApiKeyInHeaderOrQueryParamsHandler), scheme.HandlerType); - var apiKeyOptionsSnapshot = services.GetService>(); + var apiKeyOptionsSnapshot = services.GetService>(); var apiKeyOptions = apiKeyOptionsSnapshot?.Get(scheme.Name); Assert.NotNull(apiKeyOptions); Assert.NotNull(apiKeyOptions.Events?.OnValidateKey); Assert.Null(apiKeyOptions.ApiKeyProviderType); - var apiKeyProvider = services.GetService(); + var apiKeyProvider = services.GetService(); Assert.Null(apiKeyProvider); } @@ -92,13 +90,13 @@ public async Task Verify_challenge_www_authenticate_header() } [Fact] - public async Task Unauthorized() - { + public async Task Unauthorized() + { using var request = new HttpRequestMessage(HttpMethod.Get, TestServerBuilder.BaseUrl); using var response = await _client.SendAsync(request); Assert.False(response.IsSuccessStatusCode); Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); - } + } [Fact] public async Task InHeader_success() @@ -231,39 +229,39 @@ public async Task TApiKeyProvider_InQueryParams_invalid_key_unauthotized() Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } - protected virtual void Dispose(bool disposing) - { - if (!_disposedValue) - { - if (disposing) - { - // TODO: dispose managed state (managed objects) - - _client?.Dispose(); - _server?.Dispose(); - - _clientWithProvider?.Dispose(); - _serverWithProvider?.Dispose(); - } - - // TODO: free unmanaged resources (unmanaged objects) and override finalizer - // TODO: set large fields to null - _disposedValue = true; - } - } - - // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources - // ~ApiKeyInHeaderOrQueryParamsHandlerTests() - // { - // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - // Dispose(disposing: false); - // } - - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + // TODO: dispose managed state (managed objects) + + _client?.Dispose(); + _server?.Dispose(); + + _clientWithProvider?.Dispose(); + _serverWithProvider?.Dispose(); + } + + // TODO: free unmanaged resources (unmanaged objects) and override finalizer + // TODO: set large fields to null + _disposedValue = true; + } + } + + // // TODO: override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~ApiKeyInHeaderOrQueryParamsHandlerTests() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } } diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInQueryParamsHandlerTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInQueryParamsHandlerTests.cs index 92a4fb7..b8db223 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInQueryParamsHandlerTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInQueryParamsHandlerTests.cs @@ -6,24 +6,22 @@ using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using System; using System.Net; using System.Net.Http; -using System.Threading.Tasks; using Xunit; namespace AspNetCore.Authentication.ApiKey.Tests { - public class ApiKeyInQueryParamsHandlerTests : IDisposable - { + public class ApiKeyInQueryParamsHandlerTests : IDisposable + { private readonly TestServer _server; - private readonly HttpClient _client; - private readonly TestServer _serverWithProvider; - private readonly HttpClient _clientWithProvider; - private bool _disposedValue; + private readonly HttpClient _client; + private readonly TestServer _serverWithProvider; + private readonly HttpClient _clientWithProvider; + private bool _disposedValue; - public ApiKeyInQueryParamsHandlerTests() - { + public ApiKeyInQueryParamsHandlerTests() + { _server = TestServerBuilder.BuildInQueryParamsServer(); _client = _server.CreateClient(); @@ -91,13 +89,13 @@ public async Task Verify_challenge_www_authenticate_header() } [Fact] - public async Task Unauthorized() - { + public async Task Unauthorized() + { using var request = new HttpRequestMessage(HttpMethod.Get, TestServerBuilder.BaseUrl); using var response = await _client.SendAsync(request); Assert.False(response.IsSuccessStatusCode); Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); - } + } [Fact] public async Task Success() @@ -150,39 +148,39 @@ public async Task TApiKeyProvider_invalid_key_unauthotized() Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } - protected virtual void Dispose(bool disposing) - { - if (!_disposedValue) - { - if (disposing) - { - // dispose managed state (managed objects) - - _client?.Dispose(); - _server?.Dispose(); - - _clientWithProvider?.Dispose(); - _serverWithProvider?.Dispose(); - } - - // free unmanaged resources (unmanaged objects) and override finalizer - // set large fields to null - _disposedValue = true; - } - } - - // // override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources - // ~ApiKeyInQueryParamsHandlerTests() - // { - // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - // Dispose(disposing: false); - // } - - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + // dispose managed state (managed objects) + + _client?.Dispose(); + _server?.Dispose(); + + _clientWithProvider?.Dispose(); + _serverWithProvider?.Dispose(); + } + + // free unmanaged resources (unmanaged objects) and override finalizer + // set large fields to null + _disposedValue = true; + } + } + + // // override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~ApiKeyInQueryParamsHandlerTests() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } } diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInRouteValuesHandlerTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInRouteValuesHandlerTests.cs index 0c4d82b..603c612 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInRouteValuesHandlerTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyInRouteValuesHandlerTests.cs @@ -14,16 +14,16 @@ namespace AspNetCore.Authentication.ApiKey.Tests { - public class ApiKeyInRouteValuesHandlerTests : IDisposable - { + public class ApiKeyInRouteValuesHandlerTests : IDisposable + { private readonly TestServer _server; - private readonly HttpClient _client; - private readonly TestServer _serverWithProvider; - private readonly HttpClient _clientWithProvider; - private bool _disposedValue; + private readonly HttpClient _client; + private readonly TestServer _serverWithProvider; + private readonly HttpClient _clientWithProvider; + private bool _disposedValue; - public ApiKeyInRouteValuesHandlerTests() - { + public ApiKeyInRouteValuesHandlerTests() + { _server = TestServerBuilder.BuildInRouteValuesServer(); _client = _server.CreateClient(); @@ -91,13 +91,13 @@ public async Task Verify_challenge_www_authenticate_header() } [Fact] - public async Task Unauthorized() - { + public async Task Unauthorized() + { using var request = new HttpRequestMessage(HttpMethod.Get, TestServerBuilder.BaseUrl); using var response = await _client.SendAsync(request); Assert.False(response.IsSuccessStatusCode); Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); - } + } [Fact] public async Task Success() @@ -150,40 +150,40 @@ public async Task TApiKeyProvider_invalid_key_unauthotized() Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode); } - protected virtual void Dispose(bool disposing) - { - if (!_disposedValue) - { - if (disposing) - { - // dispose managed state (managed objects) - - _client?.Dispose(); - _server?.Dispose(); - - _clientWithProvider?.Dispose(); - _serverWithProvider?.Dispose(); - } - - // free unmanaged resources (unmanaged objects) and override finalizer - // set large fields to null - _disposedValue = true; - } - } - - // // override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources - // ~ApiKeyInRouteValuesHandlerTests() - // { - // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - // Dispose(disposing: false); - // } - - public void Dispose() - { - // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method - Dispose(disposing: true); - GC.SuppressFinalize(this); - } - } + protected virtual void Dispose(bool disposing) + { + if (!_disposedValue) + { + if (disposing) + { + // dispose managed state (managed objects) + + _client?.Dispose(); + _server?.Dispose(); + + _clientWithProvider?.Dispose(); + _serverWithProvider?.Dispose(); + } + + // free unmanaged resources (unmanaged objects) and override finalizer + // set large fields to null + _disposedValue = true; + } + } + + // // override finalizer only if 'Dispose(bool disposing)' has code to free unmanaged resources + // ~ApiKeyInRouteValuesHandlerTests() + // { + // // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + // Dispose(disposing: false); + // } + + public void Dispose() + { + // Do not change this code. Put cleanup code in 'Dispose(bool disposing)' method + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + } } #endif \ No newline at end of file diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyOptionsTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyOptionsTests.cs index 2da2b78..73f9355 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyOptionsTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyOptionsTests.cs @@ -4,10 +4,8 @@ using AspNetCore.Authentication.ApiKey.Tests.Infrastructure; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; -using System; using System.Net; using System.Net.Http; -using System.Threading.Tasks; using Xunit; namespace AspNetCore.Authentication.ApiKey.Tests diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyPostConfigureOptionsTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyPostConfigureOptionsTests.cs index b11e963..1451f34 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyPostConfigureOptionsTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyPostConfigureOptionsTests.cs @@ -2,15 +2,12 @@ // Licensed under the MIT License. See LICENSE file in the project root for license information. using AspNetCore.Authentication.ApiKey.Tests.Infrastructure; -using Microsoft.Extensions.DependencyInjection; -using System; -using System.Threading.Tasks; using Xunit; namespace AspNetCore.Authentication.ApiKey.Tests { - public class ApiKeyPostConfigureOptionsTests - { + public class ApiKeyPostConfigureOptionsTests + { static readonly string KeyName = "X-API-KEY"; [Fact] diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyUtilsTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyUtilsTests.cs index 86fd24e..30e089f 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyUtilsTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/ApiKeyUtilsTests.cs @@ -1,9 +1,6 @@ // Copyright (c) Mihir Dilip. All rights reserved. // Licensed under the MIT License. See LICENSE file in the project root for license information. -using System; -using System.Collections.Generic; -using System.Linq; using System.Security.Claims; using Xunit; @@ -21,7 +18,7 @@ public static void BuildClaimsPrincipal_null_ownerName_no_exception() [Fact] public static void BuildClaimsPrincipal_null_schemeName_throws_ArgumentNullException() { - var exception = Assert.Throws(() => ApiKeyUtils.BuildClaimsPrincipal(null, null, null, null)); + var exception = Assert.Throws(() => ApiKeyUtils.BuildClaimsPrincipal(null, null!, null, null)); Assert.Contains("schemeName", exception.Message); } diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/AspNetCore.Authentication.ApiKey.Tests.csproj b/test/AspNetCore.Authentication.ApiKey.Tests/AspNetCore.Authentication.ApiKey.Tests.csproj index caea524..4d77c60 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/AspNetCore.Authentication.ApiKey.Tests.csproj +++ b/test/AspNetCore.Authentication.ApiKey.Tests/AspNetCore.Authentication.ApiKey.Tests.csproj @@ -1,7 +1,7 @@  - net8.0;net7.0;net6.0;net5.0;netcoreapp3.1;netcoreapp3.0;netcoreapp2.1;net461 + net9.0;net8.0;net7.0;net6.0;net5.0;netcoreapp3.1;netcoreapp3.0;netcoreapp2.1;net461 enable enable false @@ -21,42 +21,51 @@ - - + + runtime; build; native; contentfiles; analyzers; buildtransitive all + + + + runtime; build; native; contentfiles; analyzers; buildtransitive + all + + + + - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - - - + + + runtime; build; native; contentfiles; analyzers; buildtransitive all - + runtime; build; native; contentfiles; analyzers; buildtransitive @@ -65,7 +74,7 @@ - + runtime; build; native; contentfiles; analyzers; buildtransitive diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyAuthenticationFailedContextTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyAuthenticationFailedContextTests.cs index a450d6b..be72a18 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyAuthenticationFailedContextTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyAuthenticationFailedContextTests.cs @@ -2,16 +2,14 @@ // Licensed under the MIT License. See LICENSE file in the project root for license information. using AspNetCore.Authentication.ApiKey.Tests.Infrastructure; -using System; using System.Net; using System.Net.Http; -using System.Threading.Tasks; using Xunit; namespace AspNetCore.Authentication.ApiKey.Tests.Events { - public class ApiKeyAuthenticationFailedContext - { + public class ApiKeyAuthenticationFailedContext + { private static readonly string ExpectedExceptionMessage = $"Either {nameof(ApiKeyEvents.OnValidateKey)} delegate on configure options {nameof(ApiKeyOptions.Events)} should be set or use an extention method with type parameter of type {nameof(IApiKeyProvider)}."; [Fact] diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyAuthenticationSucceededContextTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyAuthenticationSucceededContextTests.cs index ee0748a..a88e1d8 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyAuthenticationSucceededContextTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyAuthenticationSucceededContextTests.cs @@ -38,7 +38,7 @@ public async Task ReplacePrincipal_null_throws_argument_null_exception() using var client = BuildClient( context => { - Assert.Throws(() => context.ReplacePrincipal(null)); + Assert.Throws(() => context.ReplacePrincipal(null!)); return Task.CompletedTask; } ); @@ -92,6 +92,7 @@ public async Task AddClaim() { context.AddClaim(claim); + Assert.NotNull(context.Principal); Assert.Contains(context.Principal.Claims, c => c.Type == claim.Type && c.Value == claim.Value); return Task.CompletedTask; @@ -115,6 +116,7 @@ public async Task AddClaims() { context.AddClaims(claims); + Assert.NotNull(context.Principal); Assert.Contains(context.Principal.Claims, c => c.Type == claims[0].Type && c.Value == claims[0].Value); Assert.Contains(context.Principal.Claims, c => c.Type == claims[1].Type && c.Value == claims[1].Value); diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyHandleChallengeContextTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyHandleChallengeContextTests.cs index 72da071..456ece5 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyHandleChallengeContextTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyHandleChallengeContextTests.cs @@ -4,11 +4,8 @@ using AspNetCore.Authentication.ApiKey.Tests.Infrastructure; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; -using System; -using System.Collections.Generic; using System.Net; using System.Net.Http; -using System.Threading.Tasks; using Xunit; namespace AspNetCore.Authentication.ApiKey.Tests.Events diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyHandleForbiddenContextTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyHandleForbiddenContextTests.cs index c16d927..4565d6a 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyHandleForbiddenContextTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyHandleForbiddenContextTests.cs @@ -4,11 +4,8 @@ using AspNetCore.Authentication.ApiKey.Tests.Infrastructure; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; -using System; -using System.Collections.Generic; using System.Net; using System.Net.Http; -using System.Threading.Tasks; using Xunit; namespace AspNetCore.Authentication.ApiKey.Tests.Events diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyValidateKeyContextTests.cs b/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyValidateKeyContextTests.cs index a56a8f4..cb39e64 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyValidateKeyContextTests.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/Events/ApiKeyValidateKeyContextTests.cs @@ -3,14 +3,10 @@ using AspNetCore.Authentication.ApiKey.Tests.Infrastructure; using Microsoft.AspNetCore.TestHost; -using System; -using System.Collections.Generic; -using System.Linq; using System.Net; using System.Net.Http; using System.Security.Claims; using System.Text.Json; -using System.Threading.Tasks; using Xunit; namespace AspNetCore.Authentication.ApiKey.Tests.Events diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/GlobalSuppressions.cs b/test/AspNetCore.Authentication.ApiKey.Tests/GlobalSuppressions.cs index 284d804..6c37c99 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/GlobalSuppressions.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/GlobalSuppressions.cs @@ -5,7 +5,5 @@ using System.Diagnostics.CodeAnalysis; -[assembly: SuppressMessage("Style", "IDE0290:Use primary constructor", Justification = "", Scope = "member", Target = "~M:AspNetCore.Authentication.ApiKey.Tests.Infrastructure.ClaimsPrincipalDto.#ctor(System.Security.Claims.ClaimsPrincipal)")] -[assembly: SuppressMessage("Style", "IDE0290:Use primary constructor", Justification = "", Scope = "member", Target = "~M:AspNetCore.Authentication.ApiKey.Tests.Infrastructure.ClaimDto.#ctor(System.Security.Claims.Claim)")] -[assembly: SuppressMessage("Style", "IDE0290:Use primary constructor", Justification = "", Scope = "member", Target = "~M:AspNetCore.Authentication.ApiKey.Tests.Infrastructure.FakeApiKey.#ctor(System.String,System.String,System.Collections.Generic.IReadOnlyCollection{System.Security.Claims.Claim})")] -[assembly: SuppressMessage("Style", "IDE0305:Simplify collection initialization", Justification = "", Scope = "member", Target = "~M:AspNetCore.Authentication.ApiKey.Tests.ApiKeyHandlerBaseTests.MultiScheme~System.Threading.Tasks.Task")] +[assembly: SuppressMessage("Style", "IDE0290:Use primary constructor", Justification = "")] +[assembly: SuppressMessage("Maintainability", "CA1510:Use ArgumentNullException throw helper", Justification = "")] diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/Infrastructure/ClaimsPrincipalDto.cs b/test/AspNetCore.Authentication.ApiKey.Tests/Infrastructure/ClaimsPrincipalDto.cs index 48ee328..8d27f0e 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/Infrastructure/ClaimsPrincipalDto.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/Infrastructure/ClaimsPrincipalDto.cs @@ -1,9 +1,6 @@ // Copyright (c) Mihir Dilip. All rights reserved. // Licensed under the MIT License. See LICENSE file in the project root for license information. -using System; -using System.Collections.Generic; -using System.Linq; using System.Security.Claims; using System.Security.Principal; diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/Infrastructure/FakeApiKeyProvider.cs b/test/AspNetCore.Authentication.ApiKey.Tests/Infrastructure/FakeApiKeyProvider.cs index 62a2808..adce49d 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/Infrastructure/FakeApiKeyProvider.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/Infrastructure/FakeApiKeyProvider.cs @@ -1,11 +1,7 @@ // Copyright (c) Mihir Dilip. All rights reserved. // Licensed under the MIT License. See LICENSE file in the project root for license information. -using System; -using System.Collections.Generic; -using System.Linq; using System.Security.Claims; -using System.Threading.Tasks; namespace AspNetCore.Authentication.ApiKey.Tests.Infrastructure { @@ -42,7 +38,7 @@ public FakeApiKey(string key, string ownerName, IReadOnlyCollection? clai { Key = key; OwnerName = ownerName; - Claims = claims ?? new List(); + Claims = claims ?? []; } public string Key { get; } @@ -66,12 +62,12 @@ class FakeApiKeys internal static Claim FakeNameIdentifierClaim = new(ClaimTypes.NameIdentifier, "FakeNameIdentifierClaim", ClaimValueTypes.String); internal static Claim FakeRoleClaim = new(ClaimTypes.Role, "FakeRoleClaim", ClaimValueTypes.String); - internal static List Keys => new() - { - new FakeApiKey(FakeKey, FakeKeyOwner, new List { FakeNameClaim, FakeNameIdentifierClaim, FakeRoleClaim }), - new FakeApiKey(FakeKeyThrowsNotImplemented, FakeKeyOwner, new List { FakeNameClaim, FakeNameIdentifierClaim, FakeRoleClaim }), - new FakeApiKey(FakeKeyForLegacyIgnoreExtraValidatedApiKeyCheck, FakeKeyOwner, new List { FakeNameClaim, FakeNameIdentifierClaim, FakeRoleClaim }), - new FakeApiKey(FakeKeyIgnoreAuthenticationIfAllowAnonymous, FakeKeyOwner, new List { FakeNameClaim, FakeNameIdentifierClaim, FakeRoleClaim }) - }; + internal static List Keys => + [ + new FakeApiKey(FakeKey, FakeKeyOwner, [FakeNameClaim, FakeNameIdentifierClaim, FakeRoleClaim]), + new FakeApiKey(FakeKeyThrowsNotImplemented, FakeKeyOwner, [FakeNameClaim, FakeNameIdentifierClaim, FakeRoleClaim]), + new FakeApiKey(FakeKeyForLegacyIgnoreExtraValidatedApiKeyCheck, FakeKeyOwner, [FakeNameClaim, FakeNameIdentifierClaim, FakeRoleClaim]), + new FakeApiKey(FakeKeyIgnoreAuthenticationIfAllowAnonymous, FakeKeyOwner, [FakeNameClaim, FakeNameIdentifierClaim, FakeRoleClaim]) + ]; } } diff --git a/test/AspNetCore.Authentication.ApiKey.Tests/Infrastructure/TestServerBuilder.cs b/test/AspNetCore.Authentication.ApiKey.Tests/Infrastructure/TestServerBuilder.cs index d8bacd0..059a4d0 100644 --- a/test/AspNetCore.Authentication.ApiKey.Tests/Infrastructure/TestServerBuilder.cs +++ b/test/AspNetCore.Authentication.ApiKey.Tests/Infrastructure/TestServerBuilder.cs @@ -6,15 +6,10 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Mvc; -using Microsoft.AspNetCore.Mvc.Authorization; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Primitives; -using System; -using System.Linq; using System.Text.Json; -using System.Threading.Tasks; namespace AspNetCore.Authentication.ApiKey.Tests.Infrastructure {