Skip to content

Commit

Permalink
Add KeycloakFormatBinder
Browse files Browse the repository at this point in the history
  • Loading branch information
NikiforovAll committed Apr 23, 2024
1 parent 6ce3792 commit b68e4d8
Show file tree
Hide file tree
Showing 8 changed files with 73 additions and 47 deletions.
50 changes: 28 additions & 22 deletions samples/AuthGettingStarted/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using Api;
using Keycloak.AuthServices.Authentication;
using Keycloak.AuthServices.Authorization;
using Keycloak.AuthServices.Common;
using Keycloak.AuthServices.Sdk.Admin;

var builder = WebApplication.CreateBuilder(args);
Expand All @@ -12,29 +13,32 @@

host.ConfigureLogger();

services
.AddEndpointsApiExplorer()
.AddSwagger();
services.AddEndpointsApiExplorer().AddSwagger();

var authenticationOptions = configuration
.GetSection(KeycloakAuthenticationOptions.Section)
.Get<KeycloakAuthenticationOptions>();
.Get<KeycloakAuthenticationOptions>(KeycloakInstallationOptions.KeycloakFormatBinder);

services.AddKeycloakAuthentication(authenticationOptions);
services.AddKeycloakAuthentication(authenticationOptions!);

var authorizationOptions = configuration
.GetSection(KeycloakProtectionClientOptions.Section)
.Get<KeycloakProtectionClientOptions>();
.Get<KeycloakProtectionClientOptions>(KeycloakInstallationOptions.KeycloakFormatBinder);

services
.AddAuthorization(o => o.AddPolicy("IsAdmin", b =>
{
b.RequireRealmRoles("admin");
b.RequireResourceRoles("r-admin");
// TokenValidationParameters.RoleClaimType is overriden
// by KeycloakRolesClaimsTransformation
b.RequireRole("r-admin");
}))
.AddAuthorization(o =>
o.AddPolicy(
"IsAdmin",
b =>
{
b.RequireRealmRoles("admin");
b.RequireResourceRoles("r-admin");
// TokenValidationParameters.RoleClaimType is overriden
// by KeycloakRolesClaimsTransformation
b.RequireRole("r-admin");
}
)
)
.AddKeycloakAuthorization(authorizationOptions);

Check warning on line 42 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Possible null reference argument for parameter 'keycloakOptions' in 'IServiceCollection ServiceCollectionExtensions.AddKeycloakAuthorization(IServiceCollection services, KeycloakProtectionClientOptions keycloakOptions)'.

Check warning on line 42 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Possible null reference argument for parameter 'keycloakOptions' in 'IServiceCollection ServiceCollectionExtensions.AddKeycloakAuthorization(IServiceCollection services, KeycloakProtectionClientOptions keycloakOptions)'.

Check warning on line 42 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Possible null reference argument for parameter 'keycloakOptions' in 'IServiceCollection ServiceCollectionExtensions.AddKeycloakAuthorization(IServiceCollection services, KeycloakProtectionClientOptions keycloakOptions)'.

Check warning on line 42 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-macOS-latest

Possible null reference argument for parameter 'keycloakOptions' in 'IServiceCollection ServiceCollectionExtensions.AddKeycloakAuthorization(IServiceCollection services, KeycloakProtectionClientOptions keycloakOptions)'.

Check warning on line 42 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-macOS-latest

Possible null reference argument for parameter 'keycloakOptions' in 'IServiceCollection ServiceCollectionExtensions.AddKeycloakAuthorization(IServiceCollection services, KeycloakProtectionClientOptions keycloakOptions)'.

Check warning on line 42 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Possible null reference argument for parameter 'keycloakOptions' in 'IServiceCollection ServiceCollectionExtensions.AddKeycloakAuthorization(IServiceCollection services, KeycloakProtectionClientOptions keycloakOptions)'.

Check warning on line 42 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Possible null reference argument for parameter 'keycloakOptions' in 'IServiceCollection ServiceCollectionExtensions.AddKeycloakAuthorization(IServiceCollection services, KeycloakProtectionClientOptions keycloakOptions)'.

Check warning on line 42 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Possible null reference argument for parameter 'keycloakOptions' in 'IServiceCollection ServiceCollectionExtensions.AddKeycloakAuthorization(IServiceCollection services, KeycloakProtectionClientOptions keycloakOptions)'.

var adminClientOptions = configuration
Expand All @@ -45,17 +49,19 @@

var app = builder.Build();

app
.UseSwagger()
.UseSwaggerUI();
app.UseSwagger().UseSwaggerUI();

app.UseAuthentication();
app.UseAuthorization();

app.MapGet("/", (ClaimsPrincipal user) =>
{
// TokenValidationParameters.NameClaimType is overriden based on keycloak specific claim
app.Logger.LogInformation("{@User}", user.Identity.Name);
}).RequireAuthorization("IsAdmin");
app.MapGet(
"/",
(ClaimsPrincipal user) =>
{
// TokenValidationParameters.NameClaimType is overriden based on keycloak specific claim
app.Logger.LogInformation("{@User}", user.Identity.Name);

Check warning on line 62 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Dereference of a possibly null reference.

Check warning on line 62 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

For improved performance, use the LoggerMessage delegates instead of calling 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])'

Check warning on line 62 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Dereference of a possibly null reference.

Check warning on line 62 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

For improved performance, use the LoggerMessage delegates instead of calling 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])'

Check failure

Code scanning / CodeQL

Log entries created from user input High

This log entry depends on a
user-provided value
.

Check warning on line 62 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-macOS-latest

Dereference of a possibly null reference.

Check warning on line 62 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Dereference of a possibly null reference.

Check warning on line 62 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

For improved performance, use the LoggerMessage delegates instead of calling 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])'

Check warning on line 62 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Dereference of a possibly null reference.

Check warning on line 62 in samples/AuthGettingStarted/Program.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

For improved performance, use the LoggerMessage delegates instead of calling 'LoggerExtensions.LogInformation(ILogger, string?, params object?[])'
}
)
.RequireAuthorization("IsAdmin");

app.Run();
34 changes: 21 additions & 13 deletions samples/AuthZGettingStarted/ServiceCollectionExtensions.OpenApi.cs
Original file line number Diff line number Diff line change
@@ -1,19 +1,21 @@
namespace Microsoft.Extensions.DependencyInjection;

using Keycloak.AuthServices.Authentication;
using Keycloak.AuthServices.Common;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.OpenApi.Models;

public static partial class ServiceCollectionExtensions
{
public static IServiceCollection AddApplicationSwagger(this IServiceCollection services, IConfiguration configuration)
public static IServiceCollection AddApplicationSwagger(
this IServiceCollection services,
IConfiguration configuration
)
{
KeycloakAuthenticationOptions options = new();

configuration
var options = configuration
.GetSection(KeycloakAuthenticationOptions.Section)
.Bind(options, opt => opt.BindNonPublicProperties = true);
.Get<KeycloakAuthenticationOptions>(KeycloakInstallationOptions.KeycloakFormatBinder);

services.AddEndpointsApiExplorer();
services.AddSwaggerGen(c =>
Expand All @@ -31,28 +33,34 @@ public static IServiceCollection AddApplicationSwagger(this IServiceCollection s
{
Implicit = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri($"{options.KeycloakUrlRealm}/protocol/openid-connect/auth"),
TokenUrl = new Uri($"{options.KeycloakUrlRealm}/protocol/openid-connect/token"),
AuthorizationUrl = new Uri(
$"{options.KeycloakUrlRealm}/protocol/openid-connect/auth"

Check warning on line 37 in samples/AuthZGettingStarted/ServiceCollectionExtensions.OpenApi.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Dereference of a possibly null reference.

Check warning on line 37 in samples/AuthZGettingStarted/ServiceCollectionExtensions.OpenApi.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Dereference of a possibly null reference.

Check warning on line 37 in samples/AuthZGettingStarted/ServiceCollectionExtensions.OpenApi.cs

View workflow job for this annotation

GitHub Actions / Build-ubuntu-latest

Dereference of a possibly null reference.

Check warning on line 37 in samples/AuthZGettingStarted/ServiceCollectionExtensions.OpenApi.cs

View workflow job for this annotation

GitHub Actions / Build-macOS-latest

Dereference of a possibly null reference.

Check warning on line 37 in samples/AuthZGettingStarted/ServiceCollectionExtensions.OpenApi.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Dereference of a possibly null reference.

Check warning on line 37 in samples/AuthZGettingStarted/ServiceCollectionExtensions.OpenApi.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Dereference of a possibly null reference.

Check warning on line 37 in samples/AuthZGettingStarted/ServiceCollectionExtensions.OpenApi.cs

View workflow job for this annotation

GitHub Actions / Build-windows-latest

Dereference of a possibly null reference.
),
TokenUrl = new Uri(
$"{options.KeycloakUrlRealm}/protocol/openid-connect/token"
),
Scopes = new Dictionary<string, string>(),
}
}
};
c.AddSecurityDefinition(securityScheme.Reference.Id, securityScheme);
c.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{securityScheme, Array.Empty<string>()}
});
c.AddSecurityRequirement(
new OpenApiSecurityRequirement { { securityScheme, Array.Empty<string>() } }
);
});
return services;
}

public static IApplicationBuilder UseApplicationSwagger(this IApplicationBuilder app, IConfiguration configuration)
public static IApplicationBuilder UseApplicationSwagger(
this IApplicationBuilder app,
IConfiguration configuration
)
{
KeycloakAuthenticationOptions options = new();

configuration
.GetSection(KeycloakAuthenticationOptions.Section)
.Bind(options, opt => opt.BindNonPublicProperties = true);
.Bind(options, KeycloakInstallationOptions.KeycloakFormatBinder);

app.UseSwagger();
app.UseSwaggerUI(s => s.OAuthClientId(options.Resource));
Expand Down
1 change: 1 addition & 0 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
<Project>
<ItemGroup Label="Core">
<PackageVersion Include="Microsoft.Extensions.Configuration.Abstractions" Version="8.0.0"/>
<PackageVersion Include="Microsoft.Extensions.Configuration.Binder" Version="8.0.0"/>

<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="6.0.29"/>
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="8.0.1"/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ namespace Keycloak.AuthServices.Authentication;

using Claims;
using Configuration;
using Keycloak.AuthServices.Common;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.Extensions.Configuration;
Expand Down Expand Up @@ -75,7 +76,7 @@ public static AuthenticationBuilder AddKeycloakAuthentication(
{
var authenticationOptions = configuration
.GetSection(KeycloakAuthenticationOptions.Section)
.Get<KeycloakAuthenticationOptions>(options => options.BindNonPublicProperties = true);
.Get<KeycloakAuthenticationOptions>(KeycloakInstallationOptions.KeycloakFormatBinder)!;

return services.AddKeycloakAuthentication(authenticationOptions, configureOptions);
}
Expand All @@ -97,7 +98,7 @@ public static AuthenticationBuilder AddKeycloakAuthentication(
{
var authenticationOptions = configuration
.GetSection(keycloakClientSectionName)
.Get<KeycloakAuthenticationOptions>(options => options.BindNonPublicProperties = true);
.Get<KeycloakAuthenticationOptions>(KeycloakInstallationOptions.KeycloakFormatBinder)!;

return services.AddKeycloakAuthentication(authenticationOptions, configureOptions);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
namespace Keycloak.AuthServices.Authorization;

using Keycloak.AuthServices.Common;
using Microsoft.AspNetCore.Authorization;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
Expand All @@ -17,7 +18,8 @@ public static class ServiceCollectionExtensions
/// <param name="keycloakOptions"></param>
public static IServiceCollection AddKeycloakAuthorization(
this IServiceCollection services,
KeycloakProtectionClientOptions keycloakOptions)
KeycloakProtectionClientOptions keycloakOptions
)
{
services.AddKeycloakProtectionHttpClient(keycloakOptions);

Expand All @@ -40,13 +42,12 @@ public static IServiceCollection AddKeycloakAuthorization(
public static IServiceCollection AddKeycloakAuthorization(
this IServiceCollection services,
IConfiguration configuration,
string? keycloakClientSectionName = default)
string? keycloakClientSectionName = default
)
{
KeycloakProtectionClientOptions options = new();

configuration
var options = configuration
.GetSection(keycloakClientSectionName ?? KeycloakProtectionClientOptions.Section)
.Bind(options, opt => opt.BindNonPublicProperties = true);
.Get<KeycloakProtectionClientOptions>(KeycloakInstallationOptions.KeycloakFormatBinder)!;

services.AddKeycloakAuthorization(options);
return services;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Configuration.Abstractions"/>
<PackageReference Include="Microsoft.Extensions.Configuration.Binder"/>
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,14 @@ private static string NormalizeUrl(string url)
[ConfigurationKeyName("RolesSource")]
public RolesClaimTransformationSource RolesSource { get; set; } =
RolesClaimTransformationSource.ResourceAccess;

/// <summary>
/// Default binder required to handle kebab-case configuration format used by Keycloak
/// </summary>
#pragma warning disable CA2211 // Non-constant fields should not be visible
public static Action<BinderOptions> KeycloakFormatBinder = options =>
options.BindNonPublicProperties = true;
#pragma warning restore CA2211 // Non-constant fields should not be visible
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

public class KeycloakInstallationOptionsTests
{
private static readonly KeycloakInstallationOptions _expected =
private static readonly KeycloakInstallationOptions Expected =
new()
{
Realm = "Test",
Expand All @@ -23,9 +23,9 @@ public void TestKebabCaseNotation()

var authenticationOptions = configuration
.GetSection("Keycloak1")
.Get<KeycloakInstallationOptions>(options => options.BindNonPublicProperties = true);
.Get<KeycloakInstallationOptions>(KeycloakInstallationOptions.KeycloakFormatBinder);

authenticationOptions.Should().BeEquivalentTo(_expected);
authenticationOptions.Should().BeEquivalentTo(Expected);
}

[Fact]
Expand All @@ -37,6 +37,6 @@ public void TestPascalCaseNotation()
.GetSection("Keycloak2")
.Get<KeycloakInstallationOptions>();

authenticationOptions.Should().BeEquivalentTo(_expected);
authenticationOptions.Should().BeEquivalentTo(Expected);
}
}

0 comments on commit b68e4d8

Please sign in to comment.