JamesRandall / FunctionMonkey

Write more elegant Azure Functions with less boilerplate, more consistency, and support for REST APIs. Docs can be found at https://functionmonkey.azurefromthetrenches.com
MIT License
294 stars 50 forks source link

Is it possible to provide Authorization Configs outside of Compile Time? #83

Closed ptdel closed 5 years ago

ptdel commented 5 years ago

Hello, We are happily using Function Monkey to simplify our Azure Functions, and have just started using the authorization functionality. Currently, when we want to add in configurations for something like a .well-known jwks uri, this must be provided at compile time (to the best of our knowledge). We would like to have a config class that binds to some environment variables, or configuration files, that provides the authorization configurations to Function Monkey. At the moment this doesn't seem possible, as the variables pulled in by a config class would not be available at compile time. Is there any way around this, or any chance for a feature allowing this to be implemented? Essentially trying to change from doing something like:

private const string WellKnown = "our.well.known.jwks.endpoint.com";

to something along the lines of:

private static string WellKnown = Config.Authorization.WellKnown;
JamesRandall commented 5 years ago

There are a couple of options depending on how you've structured the token validator code.

If you're using the static constructor as per my gist:

static BearerTokenValidator()
{
    string domain = Environment.GetEnvironmentVariable("domain");
    string wellKnownEndpoint = $"https://{domain}/.well-known/openid-configuration";
    var documentRetriever = new HttpDocumentRetriever { RequireHttps = wellKnownEndpoint.StartsWith("https://") };
    ConfigurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
        wellKnownEndpoint,
        new OpenIdConnectConfigurationRetriever(),
        documentRetriever
    );
}

Then your only real option is to read it from the environment variables as shown above as the static constructor doesn't really have access to much of anything.

Again if you're using the recent FunctionMonkey.TokenValidator package that needs to be setup before the dependency injector is created and so again based on where you would specify this you'd be best to use Environment.GetEnvironmentVariable:

.Authorization(authorization => authorization
                .AuthorizationDefault(AuthorizationTypeEnum.TokenValidation)
                .AddOpenIdConnectTokenValidator(
                    $"https://{Environment.GetEnvironmentVariable("domain")}/.well-known/openid-configuration",
                    Audience
                )
                .Claims(claims => claims
                    .MapClaimToCommandProperty<AddPostCommand>(
                        ClaimTypes.NameIdentifier,
                        cmd => cmd.UserId
                    )
                )
            )

(there's a demo of this approach here: https://github.com/JamesRandall/FunctionMonkeyVideoSeries/tree/master/Part5)

In both examples I use the environment variable approach as per the Azure Functions docs here: https://docs.microsoft.com/en-gb/azure/azure-functions/functions-dotnet-class-library#environment-variables

In a future release I plan to update how the FunctionMonkey.TokenValidator package is registered and when I do that that will allow for the injection of configuration. I couldn't do it on v3.x as its Ould have meant updating the major version and a breaking change - which I was keen to avoid until I could accompany it with some other stuff I have planned.

Does that help?

(and I'm glad Function Monkey is proving useful!)

ptdel commented 5 years ago

@JamesRandall thank you for this, this definitely answers my questions with several options that should work for us. Thanks for the thorough reply! We should be able to leverage one of these methods until a future release. Again thanks for the great framework, I've been enjoying focusing on business logic.