Skip to content

Commit edbf815

Browse files
committed
add open api support
1 parent 2862569 commit edbf815

File tree

7 files changed

+140
-10
lines changed

7 files changed

+140
-10
lines changed

AspNetCore.SecurityKey.sln

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Middleware", "sample
3232
EndProject
3333
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Sample.Shared", "samples\Sample.Shared\Sample.Shared.csproj", "{CF5ED041-2229-4481-8DCC-8BB4438B222C}"
3434
EndProject
35+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNetCore.SecurityKey.OpenApi", "src\AspNetCore.SecurityKey.OpenApi\AspNetCore.SecurityKey.OpenApi.csproj", "{D85383E1-69CE-4CC0-B65A-171E0B31F336}"
36+
EndProject
3537
Global
3638
GlobalSection(SolutionConfigurationPlatforms) = preSolution
3739
Debug|Any CPU = Debug|Any CPU
@@ -62,6 +64,10 @@ Global
6264
{CF5ED041-2229-4481-8DCC-8BB4438B222C}.Debug|Any CPU.Build.0 = Debug|Any CPU
6365
{CF5ED041-2229-4481-8DCC-8BB4438B222C}.Release|Any CPU.ActiveCfg = Release|Any CPU
6466
{CF5ED041-2229-4481-8DCC-8BB4438B222C}.Release|Any CPU.Build.0 = Release|Any CPU
67+
{D85383E1-69CE-4CC0-B65A-171E0B31F336}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
68+
{D85383E1-69CE-4CC0-B65A-171E0B31F336}.Debug|Any CPU.Build.0 = Debug|Any CPU
69+
{D85383E1-69CE-4CC0-B65A-171E0B31F336}.Release|Any CPU.ActiveCfg = Release|Any CPU
70+
{D85383E1-69CE-4CC0-B65A-171E0B31F336}.Release|Any CPU.Build.0 = Release|Any CPU
6571
EndGlobalSection
6672
GlobalSection(SolutionProperties) = preSolution
6773
HideSolutionNode = FALSE

README.md

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -180,3 +180,45 @@ public static class Program
180180
}
181181
}
182182
```
183+
184+
### Custom Security Key Validation
185+
186+
You can implement your own custom security key validation by implementing the `ISecurityKeyValidator` interface.
187+
188+
```c#
189+
public class CustomSecurityKeyValidator : ISecurityKeyValidator
190+
{
191+
public Task<bool> ValidateAsync(HttpContext context, string key)
192+
{
193+
// custom validation logic
194+
return Task.FromResult(true);
195+
}
196+
}
197+
```
198+
199+
Use custom security key validator
200+
201+
```c#
202+
builder.Services.AddSecurityKey<CustomSecurityKeyValidator>();
203+
```
204+
205+
### Custom Security Key Extractor
206+
207+
You can implement your own custom security key extractor by implementing the `ISecurityKeyExtractor` interface.
208+
209+
```c#
210+
public class CustomSecurityKeyExtractor : ISecurityKeyExtractor
211+
{
212+
public Task<string> ExtractAsync(HttpContext context)
213+
{
214+
// custom extraction logic
215+
return Task.FromResult("custom-key");
216+
}
217+
}
218+
```
219+
220+
Use custom security key validator and extrator
221+
222+
```c#
223+
builder.Services.AddSecurityKey<CustomSecurityKeyValidator, CustomSecurityKeyExtractor>();
224+
```

samples/Sample.MinimalApi/Program.cs

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
using System.Security.Claims;
22

33
using AspNetCore.SecurityKey;
4+
using AspNetCore.SecurityKey.OpenApi;
45

56
using Sample.Shared;
67

8+
using Scalar.AspNetCore;
9+
710
namespace Sample.MinimalApi;
811

912
public static class Program
@@ -20,14 +23,11 @@ public static void Main(string[] args)
2023

2124
builder.Services.AddSecurityKey();
2225

26+
builder.Services.AddOpenApi(options => options.AddDocumentTransformer<SecurityKeyDocumentTransformer>());
2327
builder.Services.AddEndpointsApiExplorer();
24-
builder.Services.AddSwaggerGen();
2528

2629
var app = builder.Build();
2730

28-
app.UseSwagger();
29-
app.UseSwaggerUI();
30-
3131
app.UseHttpsRedirection();
3232

3333
app.UseAuthentication();
@@ -58,6 +58,9 @@ public static void Main(string[] args)
5858
.WithName("GetCurrentUser")
5959
.WithOpenApi();
6060

61+
app.MapOpenApi();
62+
app.MapScalarApiReference();
63+
6164
app.Run();
6265
}
6366
}

samples/Sample.MinimalApi/Properties/launchSettings.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"commandName": "Project",
66
"dotnetRunMessages": true,
77
"launchBrowser": true,
8-
"launchUrl": "swagger",
8+
"launchUrl": "scalar/v1",
99
"applicationUrl": "https://localhost:7216;http://localhost:5009",
1010
"environmentVariables": {
1111
"ASPNETCORE_ENVIRONMENT": "Development"

samples/Sample.MinimalApi/Sample.MinimalApi.csproj

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,15 @@
77
</PropertyGroup>
88

99
<ItemGroup>
10+
<PackageReference Update="Bogus" Version="35.6.3" />
1011
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.4" />
11-
<PackageReference Include="Swashbuckle.AspNetCore" Version="8.1.1" />
12+
<PackageReference Include="Scalar.AspNetCore" Version="2.2.7" />
1213
</ItemGroup>
1314

1415
<ItemGroup>
16+
<ProjectReference Include="..\..\src\AspNetCore.SecurityKey.OpenApi\AspNetCore.SecurityKey.OpenApi.csproj" />
1517
<ProjectReference Include="..\..\src\AspNetCore.SecurityKey\AspNetCore.SecurityKey.csproj" />
1618
<ProjectReference Include="..\Sample.Shared\Sample.Shared.csproj" />
1719
</ItemGroup>
1820

19-
<ItemGroup>
20-
<PackageReference Update="Bogus" Version="35.6.3" />
21-
</ItemGroup>
22-
2321
</Project>
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<TargetFramework>net9.0</TargetFramework>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
</PropertyGroup>
8+
9+
<ItemGroup>
10+
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="9.0.4" />
11+
</ItemGroup>
12+
13+
<ItemGroup>
14+
<ProjectReference Include="..\AspNetCore.SecurityKey\AspNetCore.SecurityKey.csproj" />
15+
</ItemGroup>
16+
17+
</Project>
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
using Microsoft.AspNetCore.Authentication;
2+
using Microsoft.AspNetCore.OpenApi;
3+
using Microsoft.Extensions.Options;
4+
using Microsoft.OpenApi.Models;
5+
6+
namespace AspNetCore.SecurityKey.OpenApi;
7+
8+
/// <summary>
9+
/// A transformer that modifies the OpenAPI document to include security key authentication schemes.
10+
/// </summary>
11+
public class SecurityKeyDocumentTransformer(IAuthenticationSchemeProvider authenticationSchemeProvider, IOptions<SecurityKeyOptions> securityKeyOptions)
12+
: IOpenApiDocumentTransformer
13+
{
14+
/// <summary>
15+
/// Transforms the OpenAPI document to include security key authentication schemes.
16+
/// </summary>
17+
/// <param name="document">The OpenAPI document to transform.</param>
18+
/// <param name="context">The context for the OpenAPI document transformation.</param>
19+
/// <param name="cancellationToken">A token to monitor for cancellation requests.</param>
20+
/// <returns>A task that represents the asynchronous operation.</returns>
21+
public async Task TransformAsync(OpenApiDocument document, OpenApiDocumentTransformerContext context, CancellationToken cancellationToken)
22+
{
23+
var authenticationSchemes = await authenticationSchemeProvider.GetAllSchemesAsync();
24+
25+
if (!authenticationSchemes.Any(authScheme => string.Equals(authScheme.Name, SecurityKeyAuthenticationDefaults.AuthenticationScheme, StringComparison.InvariantCultureIgnoreCase)))
26+
return;
27+
28+
var headerName = securityKeyOptions.Value.HeaderName;
29+
30+
var openApiSecurityScheme1 = new OpenApiSecurityScheme
31+
{
32+
Type = SecuritySchemeType.ApiKey,
33+
Scheme = SecurityKeyAuthenticationDefaults.AuthenticationScheme,
34+
In = ParameterLocation.Header,
35+
Name = headerName
36+
};
37+
38+
var requirements = new Dictionary<string, OpenApiSecurityScheme>
39+
{
40+
["API Key"] = openApiSecurityScheme1
41+
};
42+
43+
document.Components ??= new OpenApiComponents();
44+
document.Components.SecuritySchemes = requirements;
45+
46+
foreach (var operation in document.Paths.Values.SelectMany(path => path.Operations))
47+
{
48+
var openApiSecurityScheme = new OpenApiSecurityScheme
49+
{
50+
Reference = new OpenApiReference
51+
{
52+
Id = "API Key",
53+
Type = ReferenceType.SecurityScheme
54+
}
55+
};
56+
var requirement = new OpenApiSecurityRequirement
57+
{
58+
[openApiSecurityScheme] = Array.Empty<string>()
59+
};
60+
61+
operation.Value.Security.Add(requirement);
62+
}
63+
}
64+
}

0 commit comments

Comments
 (0)