Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for custom authentication handlers #6805

Open
brettsam opened this issue Oct 16, 2020 · 8 comments
Open

Add support for custom authentication handlers #6805

brettsam opened this issue Oct 16, 2020 · 8 comments

Comments

@brettsam
Copy link
Member

Today if you try to overwrite a service that we've registered, it can cause problems. We have a fairly vague explanation of this here: https://docs.microsoft.com/en-us/azure/azure-functions/functions-dotnet-dependency-injection#overriding-host-services.

Customers that try to add their own authentication in a FunctionsStartup can run into odd errors later like

System.InvalidOperationException : No authentication handler is registered for the scheme 'ArmToken'. The registered schemes are: WebJobsAuthLevelIdentityServerAuthenticationJwt, WebJobsAuthLevelIdentityServerAuthenticationIntrospection, WebJobsAuthLevel, BearerIdentityServerAuthenticationJwt, BearerIdentityServerAuthenticationIntrospection, Bearer. Did you forget to call AddAuthentication().Add[SomeAuthHandler]("ArmToken",...)?

One example would be doing this in your startup:

builder.Services.AddAuthentication()
    .AddCookie();

We should add a way to allow auth handlers from Startup compose with the built-in functions handlers. Or another alternative may be to prevent custom handlers from impacting /admin routes.

@ghost ghost assigned soninaren Oct 16, 2020
@espray
Copy link

espray commented Oct 19, 2020

YES this would be helpful. A construction func with the AuthenticationBuilder or something similar to IFunctionsConfigurationBuilder
See #4485

@soninaren soninaren removed their assignment Nov 26, 2020
@soninaren soninaren added this to the Triaged milestone Nov 26, 2020
@espray
Copy link

espray commented Dec 22, 2020

@brettsam any update?

@hajekj
Copy link

hajekj commented Apr 14, 2021

@anthonychu @jeffhollan Would you be able to take a look at this one please? Or eventually point me to someone to discuss this with? I am more than willing to contribute to the runtime to make this work as expected (since I believe it's quite crucial for some of our use-cases of Functions). Thanks!

@kamalsivalingam
Copy link

kamalsivalingam commented May 17, 2021

I am getting this error with the latest package as well, Is there any known workaround for this issue?

@nazar-kuzo
Copy link

nazar-kuzo commented Aug 17, 2021

@brettsam @kamalsivalingam @espray
I have a workaround for you.

I drilled down investigating what is causing this issue and found out that Azure Function Host has internal logic that is calling "builder.Services.AddAuthentication()" after the client Startup.cs is executed and since in your Startup.cs is already present "builder.Services.AddAuthentication()" the internal one will be ignored.

Here is the link to internal authentication registration:

public static IServiceCollection AddWebJobsScriptHostAuthentication(this IServiceCollection services)

Here is a "AddAuthentication()" internal logic that ignores the subsequent services registration:
https://github.com/dotnet/aspnetcore/blob/0ca2ed9af69e7e334b8e3c1de1d015017f138988/src/Http/Authentication.Core/src/AuthenticationCoreServiceCollectionExtensions.cs#L20

So workaround would be to use dynamic Authentication Schema registration and not to call "builder.Services.AddAuthentication()" at startup.

Dynamic schema registration example:
https://github.com/aspnet/AuthSamples/tree/master/samples/DynamicSchemes

My use case:
I hate using out of the box EasyAuth AAD authentication and want to use "Microsoft.Identity.Web" authentication.
In order to do that I have to register all logic in startap without calling "builder.Services.AddAuthentication()" and then register extension where I inject "IAuthenticationSchemeProvider" and dynamically add custom scheme.

My old code in Startup.cs:

            builder.Services
                .AddMicrosoftIdentityWebApiAuthentication(configuration, configSectionName: nameof(AuthenticationSettings))
                .EnableTokenAcquisitionToCallDownstreamApi()
                .AddInMemoryTokenCaches();

My new code in Startup.cs:

            // avoid calling builder.Services.AddAuthentication() since it overrides internal Azure Function authentication
            new AuthenticationBuilder(builder.Services)
                .AddMicrosoftIdentityWebApi(
                    configuration,
                    configSectionName: nameof(AuthenticationSettings),
                    jwtBearerScheme: CustomAuthenticationSchemes.AadBearer)
                .EnableTokenAcquisitionToCallDownstreamApi()
                .AddInMemoryTokenCaches();
                
            builder.Services.AddWebJobs(options => { }).AddExtension<AuthenticationExtensionConfigProvider>();

Please note that I use custom scheme since Azure Function uses 3 default schemes (including Bearer) so I have to use different one for my custom authentication

AuthenticationExtensionConfigProvider.cs

public class AuthenticationExtensionConfigProvider : IExtensionConfigProvider
    {
        private readonly IAuthenticationSchemeProvider authenticationSchemeProvider;

        public AuthenticationExtensionConfigProvider(IAuthenticationSchemeProvider authenticationSchemeProvider)
        {
            this.authenticationSchemeProvider = authenticationSchemeProvider;
        }

        public void Initialize(ExtensionConfigContext context)
        {
            this.InitializeAsync().GetAwaiter().GetResult();
        }

        private async Task InitializeAsync()
        {
            var jwtScheme = await this.authenticationSchemeProvider.GetSchemeAsync(JwtBearerDefaults.AuthenticationScheme);

            if (jwtScheme == null)
            {
                this.authenticationSchemeProvider.AddScheme(new AuthenticationScheme())
            }
        }
    }

Now you can authenticate to your function with custom scheme

@espray
Copy link

espray commented Aug 17, 2021

@nazar-kuzo Awesome ✨
I have not looked at this for a few months.
I was trying to host Identity Server in an AzFunc, Yes I used Dynamic Schema #4485 (comment) as well. But, I did not use IExtensionConfigProvider like you did with adding the schema, as I had the same problem with adding the schema to the IAuthenticationSchemeProvider.

  • Do you know if isolated AzFunc have the same problem with AddAuthentication(), I assume they dont?
  • Is it safe to call AddWebJobs() in an AzFunc startup, any side effects from calling this?
    builder.Services.AddWebJobs(options => { }).AddExtension<AuthenticationExtensionConfigProvider>();

@nazar-kuzo
Copy link

@espray

  • Do you know if isolated AzFunc have the same problem with AddAuthentication(), I assume they dont?

  • Have no experience working with it but I assume it is not a problem since the hosting model is different (more like regular app service), but I dont know for sure

  • Is it safe to call AddWebJobs() in an AzFunc startup, any side effects from calling this?

  • Based on my experience - Yes, it is safe. any extensions you use like EventGrid, EventHub, etc they call it under the hood, so you should be fine.

Event Hubs - https://github.com/Azure/azure-functions-eventhubs-extension/blob/dev/src/Microsoft.Azure.WebJobs.Extensions.EventHubs/EventHubsWebJobsStartup.cs

Basically it is the same API but they implement interface so you dont need to explicitly call it

@nazar-kuzo
Copy link

@brettsam @espray
Published a nuget extension that solves Authentication/Authorization problems
https://www.nuget.org/packages/AzureFunctions.Authentication/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

7 participants