Search code examples
c#.netasp.net-coreasp.net-web-api.net-3.0

Unable to resolve service for type 'FlexibleConfiguration.Abstractions.IConfiguration' in .Net Core 3.0 Web API


I am trying to add authentication and policy based authorization to .net core 3 Web API. However, I am getting a strange error after adding the policies. I'm very new to .net. I cannot pinpoint what exactly is causing this error, it (obviously) started after I added the policies to my app and was working just fine before adding this and I can't figure out why its not working. I registered the Service as a singleton and tried it as Transient and still no luck.

InvalidOperationException: Unable to resolve service for type 'FlexibleConfiguration.Abstractions.IConfiguration' while attempting to activate 'actualizer.Security.claims.transformation.UserTransformer'.

Microsoft.Extensions.DependencyInjection.ServiceLookup.CallSiteFactory.CreateArgumentCallSites(Type serviceType, Type implementationType, CallSiteChain callSiteChain, ParameterInfo[] parameters, bool throwIfCallSiteNotFound)

startup.cs

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Okta.AspNetCore;
using Microsoft.OpenApi.Models;
using Okta.Sdk;
using Okta.Sdk.Configuration;
using Microsoft.AspNetCore.Authentication;
using actualizer.Security.claims;
using actualizer.Security.claims.transformation;

namespace actualizer {
    public class Startup {

        public Startup(IConfiguration configuration) {

            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services) {

            services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
            services.AddMvc(option => option.EnableEndpointRouting = false);

           services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();

            services.AddSwaggerGen(c => {
                c.SwaggerDoc("v1", new OpenApiInfo { Title = "Actualizer", Version = "v1" });
            });

            var client = new OktaClient(new OktaClientConfiguration {
                OktaDomain = "https://dev-xxxxxx.okta.com",
                Token = "xxxxxxxxxxxxxxxx"
            });

            services.AddSingleton<IOktaClient, OktaClient>();

            services.AddAuthentication(options => {
                options.DefaultAuthenticateScheme = OktaDefaults.ApiAuthenticationScheme;
                options.DefaultChallengeScheme = OktaDefaults.ApiAuthenticationScheme;
                options.DefaultSignInScheme = OktaDefaults.ApiAuthenticationScheme;
            })
            .AddOktaWebApi(new OktaWebApiOptions() {
                OktaDomain = "xxxxxxxxxxx"
            });

            services.AddSingleton<IClaimsTransformation, UserTransformer>();

            services.AddAuthorization(options => {
                options.AddPolicy("CanMakeAnalyticsRequests", policy => policy.RequireClaim("CanMakeAnalyticsRequests"));
            });

            services.AddCors(options => {
                options.AddPolicy("VueCorsPolicy", builder => {
                    builder
                      .AllowAnyHeader()
                      .AllowAnyMethod()
                      .AllowCredentials()
                      .WithOrigins("http://localhost:8080");
                });
            });

        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IHostEnvironment env) {
            //Swagger MIddleware
            app.UseSwagger();
            app.UseSwaggerUI(c => {
                c.SwaggerEndpoint("/swagger/v1/swagger.json", "Actualizer");
                c.RoutePrefix = string.Empty;

            });
            if (env.IsDevelopment()) {
                app.UseDeveloperExceptionPage();
            } else {
                app.UseHsts();
            }
            app.UseCors("VueCorsPolicy");
            app.UseHttpsRedirection();
            app.UseAuthentication();
            app.UseMvc();
       }
    }
}

UserTransformer.cs

using System;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using actualizer.Policy;
using FlexibleConfiguration.Abstractions;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Okta.Sdk;

namespace actualizer.Security.claims.transformation {
    public class UserTransformer : IClaimsTransformation {

        private readonly IOktaClient _oktaClient;
        IHttpContextAccessor _httpContextAccessor;

        public UserTransformer(IConfiguration config, IHttpContextAccessor httpContextAccessor, IOktaClient oktaClient) {
            _httpContextAccessor = httpContextAccessor;
            this._oktaClient = oktaClient;
        }
        public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal p) {
            var claimsIdentity = p.Identity as ClaimsIdentity;
            string _CanMakeAnalyticsRequests = "CanMakeAnalyticsRequests";

            var uid = claimsIdentity.Claims.FirstOrDefault(c => c.Type == "uid").Value;
            var user = await _oktaClient.Users.GetUserAsync(uid);

            var permissions = user.Profile["permissions"];

            if (permissions.ToString() == _CanMakeAnalyticsRequests) {
                claimsIdentity.AddClaim(new Claim(Claims.CanMakeAnalyticsRequests, string.Empty));
            }
            return p;
        }

    }
}

CanMakeAnalyticsRequests.cs

using System;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authorization;

namespace actualizer.Policy.Policies {

    public class CanMakeAnalyticsRequests : AuthorizationHandler<CanMakeAnalyticsRequests>, IAuthorizationRequirement {

        protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, CanMakeAnalyticsRequests requirement) {

            if (context.User.Claims.Any(c => c.Type == Claims.CanMakeAnalyticsRequests)) {
                context.Succeed(requirement);
            } else {
                Console.WriteLine($"{context.User.Claims} tried to call analytics api");
                context.Fail();
            }

            return Task.CompletedTask;
        }
    }
}

claims.cs

using System;
using System.Threading.Tasks;
using System.Linq;
using System.Collections.Generic;
namespace actualizer.Policy {
    public class Claims {
        public const string CanMakeAnalyticsRequests = nameof(CanMakeAnalyticsRequests);
    }
}

Solution

  • The error occurs in your UserTransformer class since you use FlexibleConfiguration.Abstractions for the injected IConfiguration config while you do not register it.

    I am not familiar about the package and you need to confirm to the owner whether it supports asp.net core 3.0 if you would like to use it.

    The simpliest way is that use using Microsoft.Extensions.Configuration; instead of using FlexibleConfiguration.Abstractions in UserTransformer.cs like what you have done in startup.cs .

    Or just remove IConfiguration config in the constructor parameter since you do no use it at all in your code.