NikiforovAll/keycloak-authorization-services-dotnet

Configuration values can not be set from environment variables

ChernyshevDS opened this issue · 2 comments

Hello. I have a web API project with docker integration. This project defines configuration in appsettings.json file which may be overriden by using environment variables in docker-compose.yaml. It works fine in most cases, because in ASP.NET environment variables have higher priority as configuration source than json files. Problem is that environment variables names can not have dashes (hyphens). I see in source code that you've tried to solve it by using alternative name for such options, e.g. AuthServerUrl:

private string? authServerUrl;

[ConfigurationKeyName("auth-server-url")]
public string AuthServerUrl
{
    get => this.authServerUrl ?? this.AuthServerUrl2;
    set => this.authServerUrl = value;
}

[ConfigurationKeyName("AuthServerUrl")]
private string AuthServerUrl2 { get; set; } = default!;

However, private properties are not considered during configuration resolution, so this approach does not work. So, now it's not possible to specify AuthServerUrl, VerifyTokenAudience, TokenClockSkew and SslRequired properties from environment variables.

The easiest way to solve the issue would be to change configuration keys' names by removing hyphens.

I tried to make the helper property public and make it invisible with Browsable attributes to maintain backwards compatibility:

private string authServerUrl = null!;

[ConfigurationKeyName("auth-server-url")]
public string AuthServerUrl
{
    get => this.authServerUrl;
    set => this.authServerUrl = value;
}

[System.ComponentModel.Browsable(false)]
[System.ComponentModel.EditorBrowsable(System.ComponentModel.EditorBrowsableState.Never)]
[System.Diagnostics.DebuggerBrowsable(System.Diagnostics.DebuggerBrowsableState.Never)]
[ConfigurationKeyName("AuthServerUrl")]
public string AuthServerUrl2
{
    get => authServerUrl;
    set => authServerUrl = value;
}

Unfortunately, it doesn't work as I've expected: configurations sources priority order is not maintained, because properties with different configuration key names obviously are not considered as the same property, so I end up with json provider winning over environment variables. So no easy fix there /___\

If you use the built-in methods for configuring Keycloak (like builder.Services.AddKeycloakAuthentication(configuration);), there's a configuration in there that allows for private properties to be bound:

KeycloakAuthenticationOptions keycloakOptions = new();

builder.Configuration
    .GetSection(KeycloakAuthenticationOptions.Section)
    .Bind(keycloakOptions, opt => opt.BindNonPublicProperties = true);

So you can either use these methods, or copy the implementation inside them while changing what you see fit for an easy workaround. And then for the environment variables, you can specify them as you would any other variable:

KEYCLOAK__AUTHSERVERURL=http://localhost:8080/
KEYCLOAK__SSLREQUIRED=none
KEYCLOAK__RESOURCE=client-id
KEYCLOAK__VERIFYTOKENAUDIENCE=true

@victor-borges Thanks, I feel quite silly now :D Indeed, I'm retreiving keycloak options manually and didn't notice the BindNonPublicProperties option.