Skip to content

Commit 7c8f9fb

Browse files
authored
6.0.1 (#28)
- net6.0 support added - Information log on handler is changed to Debug log when IgnoreAuthenticationIfAllowAnonymous is enabled - Sample project added - Readme updated - Copyright year updated on License
1 parent bd929dc commit 7c8f9fb

File tree

12 files changed

+299
-24
lines changed

12 files changed

+299
-24
lines changed

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
MIT License
22

3-
Copyright (c) 2021 Mihir Dilip
3+
Copyright (c) 2022 Mihir Dilip
44

55
Permission is hereby granted, free of charge, to any person obtaining a copy
66
of this software and associated documentation files (the "Software"), to deal

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ Easy to use and very light weight Microsoft style API Key Authentication Impleme
77

88
## .NET (Core) Frameworks Supported
99
.NET Framework 4.6.1 and/or NetStandard 2.0 onwards
10-
Multi targeted: net5.0; netcoreapp3.1; netcoreapp3.0; netstandard2.0; net461
10+
Multi targeted: net6.0; net5.0; netcoreapp3.1; netcoreapp3.0; netstandard2.0; net461
1111

1212
<br/>
1313

@@ -380,6 +380,7 @@ public void ConfigureServices(IServiceCollection services)
380380
## Release Notes
381381
| Version | &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; Notes |
382382
|---------|-------|
383+
|6.0.1 | <ul><li>net6.0 support added</li><li>Information log on handler is changed to Debug log when IgnoreAuthenticationIfAllowAnonymous is enabled</li><li>Sample project added</li><li>Readme updated</li><li>Copyright year updated on License</li></ul> |
383384
|5.1.0 | <ul><li>WWW-Authenticate challenge header now returns SchemeName as scheme part instead of ApiKeyOptions.KeyName</li><li>WWW-Authenticate challenge header now has 2 new parameters 'in' and 'key_name' in value part</li><li>ForLegacyUseKeyNameAsSchemeNameOnWWWAuthenticateHeader added to the ApiKeyOptions</li><li>In Authorization Header now able to use either SchemeName or ApiKeyOptions.KeyName when matching AuthorizationHeader Scheme</li><li>Visibility of all the handlers changed to public [#21](https://github.com/mihirdilip/aspnetcore-authentication-apikey/issues/21)</li><li>Tests added</li><li>Readme updated</li><li>Copyright year updated on License</li></ul> |
384385
|5.0.0 | <ul><li>Net 5.0 target framework added</li><li>IgnoreAuthenticationIfAllowAnonymous added to the ApiKeyOptions from netcoreapp3.0 onwards [#15](https://github.com/mihirdilip/aspnetcore-authentication-apikey/issues/15)</li></ul> |
385386
|3.1.1 | <ul><li>Ability to have ApiKey in Authorization header added</li><li>Fixed extensions methods to use correct handler [#13](https://github.com/mihirdilip/aspnetcore-authentication-apikey/issues/13)</li><li>Fixed issue with resolving of IApiKeyProvider implementation when using multiple schemes [#12](https://github.com/mihirdilip/aspnetcore-authentication-apikey/issues/12)</li></ul> |
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
5+
namespace SampleWebApi_6_0.Controllers
6+
{
7+
[Route("api/[controller]")]
8+
[ApiController]
9+
public class ValuesController : ControllerBase
10+
{
11+
// GET api/values
12+
[HttpGet]
13+
public ActionResult<IEnumerable<string>> Get()
14+
{
15+
return new string[] { "value1", "value2" };
16+
}
17+
18+
[HttpGet("claims")]
19+
public ActionResult<string> Claims()
20+
{
21+
var sb = new StringBuilder();
22+
foreach (var claim in User.Claims)
23+
{
24+
sb.AppendLine($"{claim.Type}: {claim.Value}");
25+
}
26+
return sb.ToString();
27+
}
28+
29+
[HttpGet("forbid")]
30+
public new IActionResult Forbid()
31+
{
32+
return base.Forbid();
33+
}
34+
}
35+
}

samples/SampleWebApi_6_0/Program.cs

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
using AspNetCore.Authentication.ApiKey;
2+
using Microsoft.AspNetCore.Authorization;
3+
using SampleWebApi.Repositories;
4+
using SampleWebApi.Services;
5+
6+
var builder = WebApplication.CreateBuilder(args);
7+
8+
// Add User repository to the dependency container.
9+
builder.Services.AddTransient<IApiKeyRepository, InMemoryApiKeyRepository>();
10+
11+
// Add the ApiKey scheme authentication here..
12+
// It requires Realm to be set in the options if SuppressWWWAuthenticateHeader is not set.
13+
// 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.
14+
builder.Services.AddAuthentication(ApiKeyDefaults.AuthenticationScheme)
15+
16+
// 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.
17+
// Please note if OnValidateKey delegete on options.Events is also set then this delegate will be used instead of ApiKeyProvider.*
18+
//.AddApiKeyInHeaderOrQueryParams(options =>
19+
20+
// The below AddApiKeyInHeaderOrQueryParams with type parameter will add the ApiKeyProvider to the dependency register.
21+
// Please note if OnValidateKey delegete on options.Events is also set then this delegate will be used instead of ApiKeyProvider.
22+
.AddApiKeyInHeaderOrQueryParams<ApiKeyProvider>(options =>
23+
{
24+
options.Realm = "Sample Web API";
25+
options.KeyName = "X-API-KEY";
26+
27+
//// Optional option to suppress the browser login dialog for ajax calls.
28+
//options.SuppressWWWAuthenticateHeader = true;
29+
30+
//// Optional option to ignore extra check of ApiKey string after it is validated.
31+
//options.ForLegacyIgnoreExtraValidatedApiKeyCheck = true;
32+
33+
//// Optional option to ignore authentication if AllowAnonumous metadata/filter attribute is added to an endpoint.
34+
//options.IgnoreAuthenticationIfAllowAnonymous = true;
35+
36+
//// Optional events to override the ApiKey original logic with custom logic.
37+
//// Only use this if you know what you are doing at your own risk. Any of the events can be assigned.
38+
options.Events = new ApiKeyEvents
39+
{
40+
41+
//// A delegate assigned to this property will be invoked just before validating the api key.
42+
//OnValidateKey = async (context) =>
43+
//{
44+
// // custom code to handle the api key, create principal and call Success method on context.
45+
// var apiKeyRepository = context.HttpContext.RequestServices.GetRequiredService<IApiKeyRepository>();
46+
// var apiKey = await apiKeyRepository.GetApiKeyAsync(context.ApiKey);
47+
// var isValid = apiKey != null && apiKey.Key.Equals(context.ApiKey, StringComparison.OrdinalIgnoreCase);
48+
// if (isValid)
49+
// {
50+
// context.Response.Headers.Add("ValidationCustomHeader", "From OnValidateKey");
51+
// var claims = new[]
52+
// {
53+
// new Claim(ClaimTypes.NameIdentifier, apiKey.OwnerName, ClaimValueTypes.String, context.Options.ClaimsIssuer),
54+
// new Claim(ClaimTypes.Name, apiKey.OwnerName, ClaimValueTypes.String, context.Options.ClaimsIssuer),
55+
// new Claim("CustomClaimType", "Custom Claim Value - from OnValidateKey")
56+
// };
57+
// context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name));
58+
// context.Success();
59+
// }
60+
// else
61+
// {
62+
// context.NoResult();
63+
// }
64+
//},
65+
66+
//// A delegate assigned to this property will be invoked just before validating the api key.
67+
//// NOTE: Same as above delegate but slightly different implementation which will give same result.
68+
//OnValidateKey = async (context) =>
69+
//{
70+
// // custom code to handle the api key, create principal and call Success method on context.
71+
// var apiKeyRepository = context.HttpContext.RequestServices.GetRequiredService<IApiKeyRepository>();
72+
// var apiKey = await apiKeyRepository.GetApiKeyAsync(context.ApiKey);
73+
// var isValid = apiKey != null && apiKey.Key.Equals(context.ApiKey, StringComparison.OrdinalIgnoreCase);
74+
// if (isValid)
75+
// {
76+
// context.Response.Headers.Add("ValidationCustomHeader", "From OnValidateKey");
77+
// var claims = new[]
78+
// {
79+
// new Claim("CustomClaimType", "Custom Claim Value - from OnValidateKey")
80+
// };
81+
// context.ValidationSucceeded(apiKey.OwnerName, claims); // claims are optional
82+
// }
83+
// else
84+
// {
85+
// context.ValidationFailed();
86+
// }
87+
//},
88+
89+
//// A delegate assigned to this property will be invoked before a challenge is sent back to the caller when handling unauthorized response.
90+
//OnHandleChallenge = async (context) =>
91+
//{
92+
// // custom code to handle authentication challenge unauthorized response.
93+
// context.Response.StatusCode = StatusCodes.Status401Unauthorized;
94+
// context.Response.Headers.Add("ChallengeCustomHeader", "From OnHandleChallenge");
95+
// await context.Response.WriteAsync("{\"CustomBody\":\"From OnHandleChallenge\"}");
96+
// context.Handled(); // important! do not forget to call this method at the end.
97+
//},
98+
99+
//// A delegate assigned to this property will be invoked if Authorization fails and results in a Forbidden response.
100+
//OnHandleForbidden = async (context) =>
101+
//{
102+
// // custom code to handle forbidden response.
103+
// context.Response.StatusCode = StatusCodes.Status403Forbidden;
104+
// context.Response.Headers.Add("ForbidCustomHeader", "From OnHandleForbidden");
105+
// await context.Response.WriteAsync("{\"CustomBody\":\"From OnHandleForbidden\"}");
106+
// context.Handled(); // important! do not forget to call this method at the end.
107+
//},
108+
109+
//// A delegate assigned to this property will be invoked when the authentication succeeds. It will not be called if OnValidateKey delegate is assigned.
110+
//// It can be used for adding claims, headers, etc to the response.
111+
//OnAuthenticationSucceeded = (context) =>
112+
//{
113+
// //custom code to add extra bits to the success response.
114+
// context.Response.Headers.Add("SuccessCustomHeader", "From OnAuthenticationSucceeded");
115+
// var customClaims = new List<Claim>
116+
// {
117+
// new Claim("CustomClaimType", "Custom Claim Value - from OnAuthenticationSucceeded")
118+
// };
119+
// context.AddClaims(customClaims);
120+
// //or can add like this - context.Principal.AddIdentity(new ClaimsIdentity(customClaims));
121+
// return Task.CompletedTask;
122+
//},
123+
124+
//// A delegate assigned to this property will be invoked when the authentication fails.
125+
//OnAuthenticationFailed = (context) =>
126+
//{
127+
// // custom code to handle failed authentication.
128+
// context.Fail("Failed to authenticate");
129+
// return Task.CompletedTask;
130+
//}
131+
132+
};
133+
});
134+
135+
builder.Services.AddControllers(options =>
136+
{
137+
// ALWAYS USE HTTPS (SSL) protocol in production when using ApiKey authentication.
138+
//options.Filters.Add<RequireHttpsAttribute>();
139+
140+
}); //.AddXmlSerializerFormatters() // To enable XML along with JSON;
141+
142+
// All the requests will need to be authorized.
143+
// Alternatively, add [Authorize] attribute to Controller or Action Method where necessary.
144+
builder.Services.AddAuthorization(options =>
145+
{
146+
options.FallbackPolicy = new AuthorizationPolicyBuilder()
147+
.RequireAuthenticatedUser()
148+
.Build();
149+
});
150+
151+
var app = builder.Build();
152+
153+
// Configure the HTTP request pipeline.
154+
155+
app.UseHttpsRedirection();
156+
157+
app.UseAuthentication(); // NOTE: DEFAULT TEMPLATE DOES NOT HAVE THIS, THIS LINE IS REQUIRED AND HAS TO BE ADDED!!!
158+
159+
app.UseAuthorization();
160+
161+
app.MapControllers();
162+
163+
app.Run();
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
{
2+
"$schema": "https://json.schemastore.org/launchsettings.json",
3+
"iisSettings": {
4+
"windowsAuthentication": false,
5+
"anonymousAuthentication": true,
6+
"iisExpress": {
7+
"applicationUrl": "http://localhost:3920",
8+
"sslPort": 44304
9+
}
10+
},
11+
"profiles": {
12+
"IIS Express": {
13+
"commandName": "IISExpress",
14+
"launchBrowser": true,
15+
"launchUrl": "api/values",
16+
"environmentVariables": {
17+
"ASPNETCORE_ENVIRONMENT": "Development"
18+
}
19+
},
20+
"SampleWebApi_6_0": {
21+
"commandName": "Project",
22+
"dotnetRunMessages": true,
23+
"launchBrowser": true,
24+
"launchUrl": "api/values",
25+
"applicationUrl": "https://localhost:5001;http://localhost:5000",
26+
"environmentVariables": {
27+
"ASPNETCORE_ENVIRONMENT": "Development"
28+
}
29+
}
30+
}
31+
}
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
<Project Sdk="Microsoft.NET.Sdk.Web">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net6.0</TargetFramework>
5+
<Nullable>enable</Nullable>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
</PropertyGroup>
8+
9+
<Import Project="..\SampleWebApi.Shared\SampleWebApi.Shared.projitems" Label="Shared" />
10+
11+
<ItemGroup>
12+
<PackageReference Include="AspNetCore.Authentication.ApiKey" Version="6.0.1" />
13+
</ItemGroup>
14+
15+
<!--<ItemGroup>
16+
<ProjectReference Include="..\..\src\AspNetCore.Authentication.ApiKey\AspNetCore.Authentication.ApiKey.csproj" />
17+
</ItemGroup>-->
18+
19+
</Project>
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
}
8+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"Logging": {
3+
"LogLevel": {
4+
"Default": "Information",
5+
"Microsoft.AspNetCore": "Warning"
6+
}
7+
},
8+
"AllowedHosts": "*"
9+
}

src/AspNetCore.Authentication.ApiKey.sln

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
# Visual Studio Version 16
4-
VisualStudioVersion = 16.0.29418.71
3+
# Visual Studio Version 17
4+
VisualStudioVersion = 17.0.32014.148
55
MinimumVisualStudioVersion = 10.0.40219.1
66
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.Authentication.ApiKey", "AspNetCore.Authentication.ApiKey\AspNetCore.Authentication.ApiKey.csproj", "{8A74EE4A-C010-44D7-9BD7-F2386EC0972E}"
77
EndProject
88
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D7951782-F429-431D-8464-3E14BBBA7903}"
99
ProjectSection(SolutionItems) = preProject
10+
key.snk = key.snk
1011
..\LICENSE = ..\LICENSE
1112
..\README.md = ..\README.md
1213
EndProjectSection
@@ -26,14 +27,17 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleWebApi_3_1", "..\samp
2627
EndProject
2728
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SampleWebApi_5_0", "..\samples\SampleWebApi_5_0\SampleWebApi_5_0.csproj", "{1E1E202B-EFB2-40FD-8271-659F36084916}"
2829
EndProject
29-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore.Authentication.ApiKey.Tests", "..\test\AspNetCore.Authentication.ApiKey.Tests\AspNetCore.Authentication.ApiKey.Tests.csproj", "{EA2A367F-2D2D-4C20-8C32-C19F67E73187}"
30+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.Authentication.ApiKey.Tests", "..\test\AspNetCore.Authentication.ApiKey.Tests\AspNetCore.Authentication.ApiKey.Tests.csproj", "{EA2A367F-2D2D-4C20-8C32-C19F67E73187}"
3031
EndProject
3132
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{A15FB7AB-5B7A-4428-BEBA-32DEE3C88C39}"
3233
EndProject
34+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SampleWebApi_6_0", "..\samples\SampleWebApi_6_0\SampleWebApi_6_0.csproj", "{D1056CEF-550A-4EEB-B12B-1093DD3125AC}"
35+
EndProject
3336
Global
3437
GlobalSection(SharedMSBuildProjectFiles) = preSolution
3538
..\samples\SampleWebApi.Shared\SampleWebApi.Shared.projitems*{1e1e202b-efb2-40fd-8271-659f36084916}*SharedItemsImports = 5
3639
..\samples\SampleWebApi.Shared\SampleWebApi.Shared.projitems*{cabeeeae-3974-4cc4-97f1-18c8d2188daf}*SharedItemsImports = 5
40+
..\samples\SampleWebApi.Shared\SampleWebApi.Shared.projitems*{d1056cef-550a-4eeb-b12b-1093dd3125ac}*SharedItemsImports = 5
3741
..\samples\SampleWebApi.Shared\SampleWebApi.Shared.projitems*{dccad406-0950-4a42-acfb-b96b575c3ebb}*SharedItemsImports = 5
3842
..\samples\SampleWebApi.Shared\SampleWebApi.Shared.projitems*{e544fb20-29f3-41f5-a78e-6164f9c43b3c}*SharedItemsImports = 13
3943
..\samples\SampleWebApi.Shared\SampleWebApi.Shared.projitems*{fd2df3ab-05c1-4145-827a-482c539b2491}*SharedItemsImports = 5
@@ -67,6 +71,10 @@ Global
6771
{EA2A367F-2D2D-4C20-8C32-C19F67E73187}.Debug|Any CPU.Build.0 = Debug|Any CPU
6872
{EA2A367F-2D2D-4C20-8C32-C19F67E73187}.Release|Any CPU.ActiveCfg = Release|Any CPU
6973
{EA2A367F-2D2D-4C20-8C32-C19F67E73187}.Release|Any CPU.Build.0 = Release|Any CPU
74+
{D1056CEF-550A-4EEB-B12B-1093DD3125AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
75+
{D1056CEF-550A-4EEB-B12B-1093DD3125AC}.Debug|Any CPU.Build.0 = Debug|Any CPU
76+
{D1056CEF-550A-4EEB-B12B-1093DD3125AC}.Release|Any CPU.ActiveCfg = Release|Any CPU
77+
{D1056CEF-550A-4EEB-B12B-1093DD3125AC}.Release|Any CPU.Build.0 = Release|Any CPU
7078
EndGlobalSection
7179
GlobalSection(SolutionProperties) = preSolution
7280
HideSolutionNode = FALSE
@@ -78,6 +86,7 @@ Global
7886
{CABEEEAE-3974-4CC4-97F1-18C8D2188DAF} = {3C777BBB-7464-43FB-A046-EA465791AB0C}
7987
{1E1E202B-EFB2-40FD-8271-659F36084916} = {3C777BBB-7464-43FB-A046-EA465791AB0C}
8088
{EA2A367F-2D2D-4C20-8C32-C19F67E73187} = {A15FB7AB-5B7A-4428-BEBA-32DEE3C88C39}
89+
{D1056CEF-550A-4EEB-B12B-1093DD3125AC} = {3C777BBB-7464-43FB-A046-EA465791AB0C}
8190
EndGlobalSection
8291
GlobalSection(ExtensibilityGlobals) = postSolution
8392
SolutionGuid = {70815049-1680-480A-BF5A-00536D6C9C20}

src/AspNetCore.Authentication.ApiKey/ApiKeyHandlerBase.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
4242
{
4343
if (IgnoreAuthenticationIfAllowAnonymous())
4444
{
45-
Logger.LogInformation("AllowAnonymous found on the endpoint so request was not authenticated.");
45+
Logger.LogDebug("AllowAnonymous found on the endpoint so request was not authenticated.");
4646
return AuthenticateResult.NoResult();
4747
}
4848

0 commit comments

Comments
 (0)