Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Calling service/repository methods in ASP.Net Core middleware

ASP.Net Core noob here...I am using an ASP.Net Core WebAPI core project using DNX451 with EF 6.

I have a requirement to implement API Key auth in our service. To do this I have created middleware that gets information from the request and proceeds with authentication. It is SUPPOSED to go to the database, get the key to match, and then return and do the validation.

Here is the middleware implemented to look at the context and get the APIKey

AuthenticationHandler

public class AuthorizationHandler
{
    private readonly RequestDelegate _next;
    private IAuthenticationService _authenticationService;

    public AuthorizationHandler(RequestDelegate next, IAuthenticationService authService)
    {
        _authenticationService = authService;
        _next = next;
    }

    public async Task Invoke(HttpContext context)
    {
        try
        {
            var apiKey = context.Request.Headers["Key"];
            var location = context.Request.Headers["Host"];
            var locationKey = _authenticationService.GetApiKey(location);

            if (apiKey == locationKey)
                await _next(context);


            context.Response.StatusCode = 403;
            context.Response.Headers.Add("WWW-Authenticate",
                new[] { "Basic" });

        }
        catch (Exception ex)
        {
            context.Response.StatusCode = 500;
            context.Response.Headers.Add("WWW-Authenticate",
                new[] { "Basic" });
        }
    }
}

Here is the startup class with context and middleware registration

public class Startup
{
    public Startup(IHostingEnvironment env)
    {
        var builder = new ConfigurationBuilder()
            .AddJsonFile("appsettings.json")
            .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true);


        builder.AddEnvironmentVariables();
        Configuration = builder.Build();
    }

    public IConfiguration Configuration { get; set; }

    // This method gets called by the runtime. Use this method to add services to the container.
    public void ConfigureServices(IServiceCollection services)
    {
        services.AddScoped(k => new DbContext(Configuration["Data:Context:ConnectionString"]));


        // Add framework services.
        services.AddMvc();
    }

    // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
    public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
    {
        loggerFactory.AddConsole(Configuration.GetSection("Logging"));
        loggerFactory.AddDebug();

        app.UseIISPlatformHandler();

        app.UseStaticFiles();

        app.RegisterAuthorizationHeader();
        app.RegisterAuthorization();

        app.UseMvc();
    }

    // Entry point for the application.
    public static void Main(string[] args) => WebApplication.Run<Startup>(args);
}

Here is Auth service

public interface IAuthenticationService
{
    string GetApiKey(string location);
}

public class AuthenticationService: IAuthenticationService
{
    private IApiKeyRepository _apiKeyRepository;
    public AuthenticationService(IApiKeyRepository repo)
    {
        _apiKeyRepository= repo;
    }

    public string GetApiKey(string location)
    {
        return _apiKeyRepository.GetApiKeyByLocation(location);
    }
}

The repo

public interface IApiRepository
{
    string GetApiKeyByLocation(string location);
}

public class ApiRepository: IApiRepository
{
    private DbContext _context;

    public ApiRepository(DbContext context)
    {
        _context = context;
    }

    public string GetApiKeyByLocation(string location)
    {
        var apiRow = _context.ApiKeyStore.FirstOrDefault(a => a.Location == location);

        return apiRow == null ? string.Empty : apiRow.APIKey;
    }
}

When attempting this I get the following error:

The context cannot be used while the model is being created. This exception may be thrown if the context is used inside the OnModelCreating method or if the same context instance is accessed by multiple threads concurrently. Note that instance members of DbContext and related classes are not guaranteed to be thread safe.

Now, when I debug this every break point is hit twice. I believe I understand WHY this issue is occurring but have no idea how to fix it.

Can someone give me an idea, please? Any better solution ideas?

like image 693
The Sheek Geek Avatar asked Mar 12 '23 08:03

The Sheek Geek


1 Answers

To use scoped dependencies in a middleware (which is necessarily a singleton by definition), the best approach is to flow it as a parameter of InvokeAsync instead of flowing it via the constructor:

public async Task Invoke(HttpContext context, IAuthenticationService authenticationService)
{
    try
    {
        var apiKey = context.Request.Headers["Key"];
        var location = context.Request.Headers["Host"];
        var locationKey = authenticationService.GetApiKey(location);

        if (apiKey == locationKey)
            await _next(context);


        context.Response.StatusCode = 403;
        context.Response.Headers.Add("WWW-Authenticate",
            new[] { "Basic" });

    }
    catch (Exception ex)
    {
        context.Response.StatusCode = 500;
        context.Response.Headers.Add("WWW-Authenticate",
            new[] { "Basic" });
    }
}
like image 93
Kévin Chalet Avatar answered Apr 06 '23 05:04

Kévin Chalet