diff --git a/Build.ps1 b/Build.ps1
index ef44e97..2568102 100644
--- a/Build.ps1
+++ b/Build.ps1
@@ -20,6 +20,11 @@ Process {
Push-Location $workingDir
Remove-Item $outputDir -Force -Recurse -ErrorAction SilentlyContinue
+ # Duende.IdentityServer fails signature validation
+ # https://github.com/DuendeSoftware/Support/issues/1352
+ # Note that the new certificate should be included in the next version of the .NET SDK (release on 2024/08/13)
+ $env:DOTNET_NUGET_SIGNATURE_VERIFICATION="false"
+
Exec { & dotnet clean -c Release }
Exec { & dotnet build -c Release }
Exec { & dotnet test -c Release --no-build --results-directory "$outputDir" --no-restore -l "trx" -l "console;verbosity=detailed" }
diff --git a/src/Directory.Build.props b/src/Directory.Build.props
index 8914483..51b89ab 100644
--- a/src/Directory.Build.props
+++ b/src/Directory.Build.props
@@ -3,23 +3,17 @@
Copyright © Workleap $([System.DateTime]::UtcNow.ToString(yyyy))
Workleap
Workleap
- https://github.com/gsoft-inc/wl-authentication-clientcredentialsgrant
Apache-2.0
- true
latest
enable
latest-All
enable
-
- true
-
-
-
+
all
- runtime; build; native; contentfiles; analyzers; buildtransitive
+ runtime; build; native; contentfiles; analyzers
all
@@ -27,15 +21,6 @@
-
-
- true
- true
-
-
-
-
-
diff --git a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/AuthenticationBuilderExtensions.cs b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/AuthenticationBuilderExtensions.cs
index 6a66a92..ac9de65 100644
--- a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/AuthenticationBuilderExtensions.cs
+++ b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/AuthenticationBuilderExtensions.cs
@@ -54,7 +54,7 @@ public static AuthenticationBuilder AddClientCredentials(this AuthenticationBuil
{
options.OperationFilter();
}
-
+
if (options.DocumentFilterDescriptors.All(x => x.Type != typeof(SecurityDefinitionDocumentFilter)))
{
options.DocumentFilter();
diff --git a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/AuthorizationExtensions.cs b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/AuthorizationExtensions.cs
index 0ca26cb..3ae695b 100644
--- a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/AuthorizationExtensions.cs
+++ b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/AuthorizationExtensions.cs
@@ -1,4 +1,4 @@
-using Workleap.AspNetCore.Authentication.ClientCredentialsGrant;
+using Workleap.AspNetCore.Authentication.ClientCredentialsGrant;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.DependencyInjection.Extensions;
@@ -49,7 +49,7 @@ public static IServiceCollection AddClientCredentialsAuthorization(this IService
.AddAuthenticationSchemes(ClientCredentialsDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.RequireClaim("scope", $"{jwtOptions.Audience}:{ScopeClaimMapping[ClientCredentialsScope.Admin]}"));
-
+
authorizationOptions.AddPolicy(
ClientCredentialsDefaults.RequireClientCredentialsPolicyName,
policy => policy
@@ -57,7 +57,7 @@ public static IServiceCollection AddClientCredentialsAuthorization(this IService
.RequireAuthenticatedUser()
.AddRequirements(new RequireClientCredentialsRequirement()));
});
-
+
services.TryAddEnumerable(ServiceDescriptor.Singleton());
return services;
diff --git a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/ClientCredentialsDefaults.cs b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/ClientCredentialsDefaults.cs
index f45393b..587a414 100644
--- a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/ClientCredentialsDefaults.cs
+++ b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/ClientCredentialsDefaults.cs
@@ -5,7 +5,7 @@ public static class ClientCredentialsDefaults
public const string AuthenticationScheme = "ClientCredentials";
internal const string AuthenticationType = "ClientCredentials";
-
+
internal const string RequireClientCredentialsPolicyName = "ClientCredentialsPolicy";
internal const string AuthorizationReadPolicy = "ClientCredentialsRead";
diff --git a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/OpenAPI/SecurityDefinitionDocumentFilter.cs b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/OpenAPI/SecurityDefinitionDocumentFilter.cs
index 010f718..20203e4 100644
--- a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/OpenAPI/SecurityDefinitionDocumentFilter.cs
+++ b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/OpenAPI/SecurityDefinitionDocumentFilter.cs
@@ -1,4 +1,4 @@
-using Microsoft.AspNetCore.Authentication.JwtBearer;
+using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Options;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.SwaggerGen;
@@ -15,7 +15,7 @@ internal sealed class SecurityDefinitionDocumentFilter(IOptionsMonitor ExtractScopes(IEnumerable permissions)
{
// Audience has already been validated as non-empty
var audience = this._jwtOptions.Audience!;
-
+
var scopes = new Dictionary
{
[SwaggerUtils.GetScopeForAnyPermission(audience)] = "Request all permissions for specified client ID",
diff --git a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/OpenAPI/SecurityRequirementOperationFilter.cs b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/OpenAPI/SecurityRequirementOperationFilter.cs
index 67ff428..ec5cbd5 100644
--- a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/OpenAPI/SecurityRequirementOperationFilter.cs
+++ b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/OpenAPI/SecurityRequirementOperationFilter.cs
@@ -1,4 +1,4 @@
-using System.Globalization;
+using System.Globalization;
using System.Text;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Http;
@@ -29,13 +29,13 @@ public void Apply(OpenApiOperation operation, OperationFilterContext context)
this.AddOperationSecurityReference(operation, attributes);
AppendScopeToOperationSummary(operation, attributes);
}
-
+
private static void AddAuthenticationAndAuthorizationErrorResponse(OpenApiOperation operation)
{
operation.Responses.TryAdd(StatusCodes.Status401Unauthorized.ToString(CultureInfo.InvariantCulture), new OpenApiResponse { Description = ReasonPhrases.GetReasonPhrase(StatusCodes.Status401Unauthorized) });
operation.Responses.TryAdd(StatusCodes.Status403Forbidden.ToString(CultureInfo.InvariantCulture), new OpenApiResponse { Description = ReasonPhrases.GetReasonPhrase(StatusCodes.Status403Forbidden) });
}
-
+
private void AddOperationSecurityReference(OpenApiOperation operation, HashSet permissions)
{
var isAlreadyReferencingSecurityDefinition = operation.Security.Any(requirement => requirement.Keys.Any(key => key.Reference?.Id == ClientCredentialsDefaults.OpenApiSecurityDefinitionId));
@@ -43,7 +43,7 @@ private void AddOperationSecurityReference(OpenApiOperation operation, HashSet 0)
{
summary.Append(". ");
}
-
+
summary.Append(requireScopeSummary);
operation.Summary = summary.ToString();
}
-
+
private IEnumerable ExtractScopes(HashSet permissions)
{
foreach (var permission in permissions)
diff --git a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/OpenAPI/SwaggerUtils.cs b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/OpenAPI/SwaggerUtils.cs
index 2d7a6ed..bdab464 100644
--- a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/OpenAPI/SwaggerUtils.cs
+++ b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/OpenAPI/SwaggerUtils.cs
@@ -1,4 +1,4 @@
-using System.Reflection;
+using System.Reflection;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.ApiExplorer;
using Swashbuckle.AspNetCore.SwaggerGen;
@@ -18,7 +18,7 @@ public static IEnumerable GetRequiredPermissions(ApiDescription apiDescr
{
return [];
}
-
+
// Controllers - Attributes on the action method (empty for minimal APIs)
attributes.AddRange(methodInfo.GetCustomAttributes(inherit: true));
}
@@ -34,7 +34,7 @@ public static string FormatScopeForSpecificPermission(string audience, string pe
{
return $"target-entity:{audience}:{permission}";
}
-
+
public static string GetScopeForAnyPermission(string audience)
{
return $"target-entity:{audience}";
diff --git a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/RequireClientCredentialsAttribute.cs b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/RequireClientCredentialsAttribute.cs
index 51bee30..0f623f9 100644
--- a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/RequireClientCredentialsAttribute.cs
+++ b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/RequireClientCredentialsAttribute.cs
@@ -1,4 +1,4 @@
-using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.CodeAnalysis;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Builder;
@@ -32,7 +32,7 @@ public RequireClientCredentialsAttribute(ClientCredentialsScope scope)
: this(EnumScopeNameMapping.GetValueOrDefault(scope) ?? throw new ArgumentOutOfRangeException(nameof(scope), scope, $"'{scope}' is not an valid scope value"))
{
}
-
+
///
/// Verifies that the endpoint is called with the right granular permissions.
/// - It will check in those claims type: scope, scp or http://schemas.microsoft.com/identity/claims/scope
diff --git a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/RequireClientCredentialsRequirementHandler.cs b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/RequireClientCredentialsRequirementHandler.cs
index 8bb8512..2ab26a8 100644
--- a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/RequireClientCredentialsRequirementHandler.cs
+++ b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/RequireClientCredentialsRequirementHandler.cs
@@ -1,4 +1,4 @@
-using System.Diagnostics.CodeAnalysis;
+using System.Diagnostics.CodeAnalysis;
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
@@ -29,14 +29,14 @@ public RequireClientCredentialsRequirementHandler(IOptionsMonitor httpContext.GetEndpoint(),
@@ -71,7 +71,7 @@ private string[] FormatScopes(string requiredPermission)
{
return [requiredPermission, $"{this._jwtOptions.Audience}:{requiredPermission}"];
}
-
+
private static bool HasRequiredScope(ClaimsPrincipal claimsPrincipal, string[] requiredScopes)
{
return claimsPrincipal.Claims
diff --git a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/Workleap.AspNetCore.Authentication.ClientCredentialsGrant.csproj b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/Workleap.AspNetCore.Authentication.ClientCredentialsGrant.csproj
index 0f9f9b8..c4d28f5 100644
--- a/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/Workleap.AspNetCore.Authentication.ClientCredentialsGrant.csproj
+++ b/src/Workleap.AspNetCore.Authentication.ClientCredentialsGrant/Workleap.AspNetCore.Authentication.ClientCredentialsGrant.csproj
@@ -1,6 +1,6 @@
- net6.0;net7.0;net8.0
+ net6.0;net8.0
true
true
snupkg
@@ -16,7 +16,6 @@
-
diff --git a/src/Workleap.Authentication.ClientCredentialsGrant.Tests/AuthorizationExtensionsTest.cs b/src/Workleap.Authentication.ClientCredentialsGrant.Tests/AuthorizationExtensionsTest.cs
index 35aebc2..20e3370 100644
--- a/src/Workleap.Authentication.ClientCredentialsGrant.Tests/AuthorizationExtensionsTest.cs
+++ b/src/Workleap.Authentication.ClientCredentialsGrant.Tests/AuthorizationExtensionsTest.cs
@@ -1,4 +1,4 @@
-using Workleap.AspNetCore.Authentication.ClientCredentialsGrant;
+using Workleap.AspNetCore.Authentication.ClientCredentialsGrant;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Authorization.Infrastructure;
@@ -50,11 +50,11 @@ public async Task GivenIServiceCollection_WhenAddClientCredentialsAuthorization_
var adminPolicy = authorizationValues.GetPolicy(ClientCredentialsDefaults.AuthorizationAdminPolicy);
ValidateClassicPolicy(adminPolicy, ClientCredentialsScope.Admin);
-
+
var requireClientCredentialsPolicy = authorizationValues.GetPolicy(ClientCredentialsDefaults.RequireClientCredentialsPolicyName);
ValidateRequireClientCredentialsPolicy(requireClientCredentialsPolicy);
}
-
+
[Fact]
public async Task GivenIServiceCollection_WhenAddClientCredentialsAuthorization_ThenRequirementHandlerRegistered()
{
@@ -91,7 +91,7 @@ private static void ValidateClassicPolicy(AuthorizationPolicy? policy, ClientCre
Assert.Equal($"{DefaultAudience}:{AuthorizationExtensions.ScopeClaimMapping[scope]}", allowedScope);
});
}
-
+
private static void ValidateRequireClientCredentialsPolicy(AuthorizationPolicy? policy)
{
Assert.NotNull(policy);
diff --git a/src/Workleap.Authentication.ClientCredentialsGrant.Tests/IntegrationTests.cs b/src/Workleap.Authentication.ClientCredentialsGrant.Tests/IntegrationTests.cs
index c769522..bae511c 100644
--- a/src/Workleap.Authentication.ClientCredentialsGrant.Tests/IntegrationTests.cs
+++ b/src/Workleap.Authentication.ClientCredentialsGrant.Tests/IntegrationTests.cs
@@ -1,4 +1,4 @@
-using System.Collections.Concurrent;
+using System.Collections.Concurrent;
using System.Net;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Stores;
@@ -152,7 +152,7 @@ public async Task Real_Client_Server_Communication()
// Using the classic policy, reading invoices should be successful because we're authenticated with a JWT that has the "invoices" audience and "invoices.read" scope
var readInvoicesResponse = await invoicesReadHttpClient.GetStringAsync("https://invoice-app.local/read-invoices", cts.Token);
Assert.Equal("This protected endpoint is for reading invoices", readInvoicesResponse);
-
+
// Using the granular policy, reading invoices should be successful because we're authenticated with a JWT that has the "invoices" audience and "invoices.read" scope
var readInvoicesGranularResponse = await invoicesReadHttpClient.GetStringAsync("https://invoice-app.local/read-invoices-granular", cts.Token);
Assert.Equal("This protected endpoint is for reading invoices", readInvoicesGranularResponse);
diff --git a/src/Workleap.Authentication.ClientCredentialsGrant.Tests/OpenAPI/OpenApiSecurityDescriptionTests.cs b/src/Workleap.Authentication.ClientCredentialsGrant.Tests/OpenAPI/OpenApiSecurityDescriptionTests.cs
index 45738d0..3cceb3e 100644
--- a/src/Workleap.Authentication.ClientCredentialsGrant.Tests/OpenAPI/OpenApiSecurityDescriptionTests.cs
+++ b/src/Workleap.Authentication.ClientCredentialsGrant.Tests/OpenAPI/OpenApiSecurityDescriptionTests.cs
@@ -1,4 +1,4 @@
-using CliWrap;
+using CliWrap;
using Meziantou.Framework;
namespace Workleap.Authentication.ClientCredentialsGrant.Tests.OpenAPI;
@@ -9,7 +9,7 @@ public class OpenApiSecurityDescriptionTests
public async Task Given_API_With_Client_Credentials_Attribute_When_Generating_OpenAPI_Then_Equal_Expected_Document()
{
var solutionPath = GetSolutionPath();
-
+
var testsFolder = Path.Combine(solutionPath, "tests");
var projectFolder = Path.Combine(testsFolder, "WebApi.OpenAPI.SystemTest");
var generatedFilePath = Path.Combine(projectFolder, "openapi-v1.yaml");
@@ -31,12 +31,12 @@ public async Task Given_API_With_Client_Credentials_Attribute_When_Generating_Op
Assert.Equal(expectedFileContent, generatedFileContent, ignoreLineEndingDifferences: true);
}
-
+
private static string GetSolutionPath()
{
return GetGitRoot() / "src";
}
-
+
private static FullPath GetGitRoot()
{
if (FullPath.CurrentDirectory().TryFindFirstAncestorOrSelf(current => Directory.Exists(current / ".git"), out var root))
diff --git a/src/Workleap.Authentication.ClientCredentialsGrant.Tests/RequireClientCredentialsAttributeTests.cs b/src/Workleap.Authentication.ClientCredentialsGrant.Tests/RequireClientCredentialsAttributeTests.cs
index 3a8d626..8f91f45 100644
--- a/src/Workleap.Authentication.ClientCredentialsGrant.Tests/RequireClientCredentialsAttributeTests.cs
+++ b/src/Workleap.Authentication.ClientCredentialsGrant.Tests/RequireClientCredentialsAttributeTests.cs
@@ -1,4 +1,4 @@
-using System.Collections;
+using System.Collections;
using Microsoft.AspNetCore.Authorization;
using Workleap.AspNetCore.Authentication.ClientCredentialsGrant;
@@ -20,7 +20,7 @@ public void GivenInvalidClassicScope_WhenCreate_ThenThrowArgumentException()
var scope = (ClientCredentialsScope)999;
Assert.Throws(() => new RequireClientCredentialsAttribute(scope));
}
-
+
[Fact]
public void GivenSinglePermission_WhenCreate_ThenSamePermission()
{
diff --git a/src/Workleap.Authentication.ClientCredentialsGrant.Tests/RequireClientCredentialsRequirementHandlerTests.cs b/src/Workleap.Authentication.ClientCredentialsGrant.Tests/RequireClientCredentialsRequirementHandlerTests.cs
index bed3881..70b6c34 100644
--- a/src/Workleap.Authentication.ClientCredentialsGrant.Tests/RequireClientCredentialsRequirementHandlerTests.cs
+++ b/src/Workleap.Authentication.ClientCredentialsGrant.Tests/RequireClientCredentialsRequirementHandlerTests.cs
@@ -1,4 +1,4 @@
-using System.Security.Claims;
+using System.Security.Claims;
using FakeItEasy;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.AspNetCore.Authorization;
@@ -35,7 +35,7 @@ public async Task GivenUserHaveTheRequiredScopesInOneOfClaimType_WhenHandleRequi
// Then
Assert.True(context.HasSucceeded);
}
-
+
[Theory]
[InlineData(true)]
[InlineData(false)]
@@ -43,13 +43,13 @@ public async Task GivenUserHaveOneOfTheRequiredScopes_WhenHandleRequirement_Then
{
// Given
var expectedAudience = "invoices";
-
+
var userClaims = new List
{
new("scope", usePrefixAudienceFormat ? $"{expectedAudience}:requiredPermission" : "requiredPermission"),
new("scope", "otherPermission"),
};
-
+
var requiredPermission = "requiredPermission";
var context = ConfigureHandlerContext(userClaims, requiredPermission);
@@ -64,7 +64,7 @@ public async Task GivenUserHaveOneOfTheRequiredScopes_WhenHandleRequirement_Then
// Then
Assert.True(context.HasSucceeded);
}
-
+
[Fact]
public async Task GivenUserDoNotHaveTheRequiredScopes_WhenHandleRequirement_ThenNotSucceeded()
{
@@ -75,8 +75,6 @@ public async Task GivenUserDoNotHaveTheRequiredScopes_WhenHandleRequirement_Then
new("scope", "otherPermission"),
};
- var requiredPermission = "randomPermission";
-
var context = ConfigureHandlerContext(userClaims, "randomPermission");
var handler = ConfigureHandler(new JwtBearerOptions());
@@ -86,12 +84,12 @@ public async Task GivenUserDoNotHaveTheRequiredScopes_WhenHandleRequirement_Then
// Then
Assert.False(context.HasSucceeded);
}
-
+
private static RequireClientCredentialsRequirementHandler ConfigureHandler(JwtBearerOptions jwtOptions)
{
var jwtOptionsMonitor = A.Fake>();
A.CallTo(() => jwtOptionsMonitor.Get(ClientCredentialsDefaults.AuthenticationScheme)).Returns(jwtOptions);
-
+
return new RequireClientCredentialsRequirementHandler(jwtOptionsMonitor);
}
@@ -108,7 +106,7 @@ private static AuthorizationHandlerContext ConfigureHandlerContext(List c
return new AuthorizationHandlerContext(new[] { new RequireClientCredentialsRequirement() }, user, httpContext);
}
-
+
private sealed class EndpointFeature : IEndpointFeature
{
public Endpoint? Endpoint { get; set; }
diff --git a/src/Workleap.Authentication.ClientCredentialsGrant.Tests/Workleap.Authentication.ClientCredentialsGrant.Tests.csproj b/src/Workleap.Authentication.ClientCredentialsGrant.Tests/Workleap.Authentication.ClientCredentialsGrant.Tests.csproj
index d7f9818..6b743eb 100644
--- a/src/Workleap.Authentication.ClientCredentialsGrant.Tests/Workleap.Authentication.ClientCredentialsGrant.Tests.csproj
+++ b/src/Workleap.Authentication.ClientCredentialsGrant.Tests/Workleap.Authentication.ClientCredentialsGrant.Tests.csproj
@@ -15,12 +15,11 @@
-
+
-
-
-
+
+
diff --git a/src/Workleap.Authentication.ClientCredentialsGrant.Tests/XunitLoggerProvider.cs b/src/Workleap.Authentication.ClientCredentialsGrant.Tests/XunitLoggerProvider.cs
index 9671b20..a511d74 100644
--- a/src/Workleap.Authentication.ClientCredentialsGrant.Tests/XunitLoggerProvider.cs
+++ b/src/Workleap.Authentication.ClientCredentialsGrant.Tests/XunitLoggerProvider.cs
@@ -1,4 +1,5 @@
-using Microsoft.Extensions.Logging;
+using System.Diagnostics.CodeAnalysis;
+using Microsoft.Extensions.Logging;
namespace Workleap.Authentication.ClientCredentialsGrant.Tests;
@@ -27,6 +28,7 @@ public ILogger CreateLogger(string categoryName)
return this;
}
+ [SuppressMessage("ApiDesign", "RS0030:Do not use banned APIs")]
public void Log(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func formatter)
{
var message = formatter(state, exception);
diff --git a/src/Workleap.Extensions.Http.Authentication.ClientCredentialsGrant/IsExternalInit.cs b/src/Workleap.Extensions.Http.Authentication.ClientCredentialsGrant/IsExternalInit.cs
index 62cf578..35f71ac 100644
--- a/src/Workleap.Extensions.Http.Authentication.ClientCredentialsGrant/IsExternalInit.cs
+++ b/src/Workleap.Extensions.Http.Authentication.ClientCredentialsGrant/IsExternalInit.cs
@@ -1,17 +1,16 @@
-#if !NET5_0_OR_GREATER
+#if !NET5_0_OR_GREATER
+
+using System.ComponentModel;
+
// ReSharper disable once CheckNamespace
-namespace System.Runtime.CompilerServices
-{
- // ReSharper disable once RedundantNameQualifier
- using System.ComponentModel;
+namespace System.Runtime.CompilerServices;
- // Compiler helper class that allows using "init" keyword in .NET Standard 2.0:
- // https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/init
- // The same thing is also done in ASP.NET Core:
- // https://github.com/dotnet/aspnetcore/blob/v6.0.0/src/Shared/IsExternalInit.cs
- [EditorBrowsable(EditorBrowsableState.Never)]
- internal static class IsExternalInit
- {
- }
+// Compiler helper class that allows using "init" keyword in .NET Standard 2.0:
+// https://learn.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/init
+// The same thing is also done in ASP.NET Core:
+// https://github.com/dotnet/aspnetcore/blob/v6.0.0/src/Shared/IsExternalInit.cs
+[EditorBrowsable(EditorBrowsableState.Never)]
+internal static class IsExternalInit
+{
}
#endif
\ No newline at end of file
diff --git a/src/Workleap.Extensions.Http.Authentication.ClientCredentialsGrant/OnStartupTokenCacheBackgroundService.cs b/src/Workleap.Extensions.Http.Authentication.ClientCredentialsGrant/OnStartupTokenCacheBackgroundService.cs
index 6890a97..8d7f584 100644
--- a/src/Workleap.Extensions.Http.Authentication.ClientCredentialsGrant/OnStartupTokenCacheBackgroundService.cs
+++ b/src/Workleap.Extensions.Http.Authentication.ClientCredentialsGrant/OnStartupTokenCacheBackgroundService.cs
@@ -1,4 +1,4 @@
-using Microsoft.Extensions.Hosting;
+using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
namespace Workleap.Extensions.Http.Authentication.ClientCredentialsGrant;
diff --git a/src/tests/WebApi.OpenAPI.SystemTest/Endpoints/ClientCredentialsController.cs b/src/tests/WebApi.OpenAPI.SystemTest/Endpoints/ClientCredentialsController.cs
index 0f1fa84..f36e2da 100644
--- a/src/tests/WebApi.OpenAPI.SystemTest/Endpoints/ClientCredentialsController.cs
+++ b/src/tests/WebApi.OpenAPI.SystemTest/Endpoints/ClientCredentialsController.cs
@@ -17,7 +17,7 @@ public IActionResult SeeCocktail(int id)
{
return this.Ok("Hello World!");
}
-
+
[HttpPost]
[Route("/controller-requires-permission")]
[SwaggerOperation(Summary = "This controller method should require the cocktail.buy permission.")]