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

Suggestion: Include a way to stub a specific authentication schema #136

Open
egil opened this issue Feb 8, 2023 · 0 comments
Open

Suggestion: Include a way to stub a specific authentication schema #136

egil opened this issue Feb 8, 2023 · 0 comments
Milestone

Comments

@egil
Copy link

egil commented Feb 8, 2023

Background: I have an API where the endpoints have a specific authentication schema specified via the Authorize attribute, e.g. [Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)] because the project supports multiple different auth schemas.

Unfortunately, that means the built-in AuthenticationStub in Alba doesn't work, since it replaces the existing schemas with the "Test" schema.

The workaround I ended up with is the following type, which you may want to include in Alba, or you may want to do something else that achieves the same goal:

public sealed class AuthenticationSchemaStub : AuthenticationExtensionBase, IAlbaExtension
{
    private const string TestSchemaName = "Test";

    internal string OverrideSchemaTargetName { get; }

    public AuthenticationSchemaStub(string overrideSchemaTargetName)
        => OverrideSchemaTargetName = overrideSchemaTargetName;

    void IDisposable.Dispose()
    {
        // nothing to dispose
    }

    ValueTask IAsyncDisposable.DisposeAsync() => ValueTask.CompletedTask;

    Task IAlbaExtension.Start(IAlbaHost host) => Task.CompletedTask;

    IHostBuilder IAlbaExtension.Configure(IHostBuilder builder)
    {
        return builder.ConfigureServices(services =>
        {
            services.AddSingleton(this);
            services.AddTransient<IAuthenticationSchemeProvider, MockSchemeProvider>();
        });
    }

    internal ClaimsPrincipal BuildPrincipal(HttpContext context)
    {
        var claims = allClaims(context);
        var identity = new ClaimsIdentity(claims, TestSchemaName);
        var principal = new ClaimsPrincipal(identity);
        return principal;
    }

    private sealed class MockSchemeProvider : AuthenticationSchemeProvider
    {
        private readonly string overrideSchemaTargetName;

        public MockSchemeProvider(AuthenticationSchemaStub authSchemaStub, IOptions<AuthenticationOptions> options)
            : base(options)
        {
            overrideSchemaTargetName = authSchemaStub.OverrideSchemaTargetName;
        }

        public override Task<AuthenticationScheme> GetSchemeAsync(string name)
        {
            if (name.Equals(overrideSchemaTargetName, StringComparison.OrdinalIgnoreCase))
            {
                var scheme = new AuthenticationScheme(
                    TestSchemaName,
                    TestSchemaName,
                    typeof(MockAuthenticationHandler));

                return Task.FromResult(scheme);
            }

            return base.GetSchemeAsync(name);
        }

        private sealed class MockAuthenticationHandler : AuthenticationHandler<AuthenticationSchemeOptions>
        {
            private readonly AuthenticationSchemaStub authenticationSchemaStub;

            public MockAuthenticationHandler(
                AuthenticationSchemaStub authenticationSchemaStub,
                IOptionsMonitor<AuthenticationSchemeOptions> options,
                ILoggerFactory logger,
                UrlEncoder encoder,
                ISystemClock clock)
                : base(options, logger, encoder, clock)
            {
                this.authenticationSchemaStub = authenticationSchemaStub;
            }

            protected override Task<AuthenticateResult> HandleAuthenticateAsync()
            {
                var principal = authenticationSchemaStub.BuildPrincipal(Context);
                var ticket = new AuthenticationTicket(principal, TestSchemaName);
                return Task.FromResult(AuthenticateResult.Success(ticket));
            }
        }
    }
}

It works much like Alba's AuthenticationStub, you can add claims to it, etc., the difference is that you specify which schema the stub should replace, so now you can have it replace a specific schema, e.g. new AuthenticationSchemaStub(JwtBearerDefaults.AuthenticationScheme).

Related to #135.

@Hawxy Hawxy modified the milestones: V5, v8 Mar 5, 2023
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

2 participants