4

I have a middleware that adds a custom roles to the user after login by using AzureAd, it works fine but I have a problem that after for example I logged in and someone also logged in after me, this user still has the same roles that I added for me. My question: Why blazor by following this way is saving this roles for different user even after signing out? I would like to understand the mechanism behind
This is the middleware

public class RoleHandler
{
    private readonly RequestDelegate _next;
    private List<string> Roles { get; set; }

    public RoleHandler(RequestDelegate Next)
    {
        _next = Next;
    }

    public async Task InvokeAsync(HttpContext context, IGenericHttpClient<Role> httpClient)
    {
        if (Roles == null || Roles.Count == 0)
        {
            Roles = await GetRole(context, httpClient);
        }
        else
        {
            foreach (var role in Roles)
            {
                //Add roles to this user, in this case user can be admin or developer ...
                context.User.Identities.FirstOrDefault().AddClaim(new Claim(ClaimTypes.Role, role));
            }
        }
        await _next(context);
    }

    public async Task<List<string>> GetRole(HttpContext context, IGenericHttpClient<Role> httpClient)
    {
        List<string> rolesList = new();
        //Get role from api like [guid, admin]
        var appUserRoles = await httpClient.GetJsonAsync("/api/roles/search?id=XXX");
        //Get role from user as guid
        var RolesString = context.User.Claims
                .Select(c => c.Value).ToList();

        foreach (var appRole in appUserRoles)
        {
            foreach (var role in RolesString)
            {
                if (appRole.RoleString == role)
                {
                    rolesList.Add(appRole.Name);
                }
            }
        }
        return rolesList;
    }
}

ConfigureServices in Startup

        public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<ILoggerManager, LoggerManager>();

        var initialScopes = Configuration.GetValue<string>("DownstreamApi:Scopes")?.Split(' ');

        JwtSecurityTokenHandler.DefaultMapInboundClaims = false;

        services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
            .AddMicrosoftIdentityWebApp(Configuration.GetSection("AzureAd"))
                .EnableTokenAcquisitionToCallDownstreamApi(initialScopes)
                    .AddMicrosoftGraph(Configuration.GetSection("DownstreamApi"))
                    .AddInMemoryTokenCaches();

        services.AddScoped(typeof(IGenericHttpClient<>), typeof(GenericHttpClient<>));

        services.AddControllersWithViews()
            .AddMicrosoftIdentityUI();

        services.AddAuthorization(options =>
        {
            // By default, all incoming requests will be authorized according to the default policy
            options.FallbackPolicy = options.DefaultPolicy;
        });

        services.AddLocalization(options => options.ResourcesPath = "Resources");
        services.AddRazorPages();
        services.AddServerSideBlazor()
            .AddMicrosoftIdentityConsentHandler();
    }

GenericHttpClient

public class GenericHttpClient<T> : IGenericHttpClient<T> where T : class
{
    private readonly IHttpClientFactory _clientFactory;
    private HttpClient _client;
    private readonly IConfiguration _configuration;
    public GenericHttpClient(IHttpClientFactory clientFactory,
        IConfiguration configuration)
    {
        _clientFactory = clientFactory;
        _configuration = configuration;

        _client = _clientFactory.CreateClient();

        _client.BaseAddress = new Uri("https://localhost");
    }
    
    public async ValueTask<List<T>> GetJsonAsync(string url)
    {
        using HttpResponseMessage response = await _client.GetAsync(url);
        ValidateResponse(response);
        var content = await ValidateContent(response).ReadAsStringAsync();
        return JsonSerializer.Deserialize<List<T>>(content, new JsonSerializerOptions() { PropertyNameCaseInsensitive=true});
    }
    // ......
}

}

Nb777
  • 1,658
  • 8
  • 27
  • Where can we find the implementation of `GenericHttpClient<>`? The behaviour might be explained by caching and reuse of the `GenericHttpClient<>`, but it is hard confirming without knowing the details of `GenericHttpClient<>`. – J.N. Dec 21 '21 at 14:34

1 Answers1

0

You should use AddScoped when you register HttpClient service in Startup.cs file.

Related Post:

1. AddTransient, AddScoped and AddSingleton Services Differences

2. Cannot Consume Scoped Service From Singleton – A Lesson In ASP.NET Core DI Scopes

Jason Pan
  • 15,263
  • 1
  • 14
  • 29
  • Thanks @Jason Pan for your answer, I added `ConfigureServices` of `Startup`, I think that I used AddScoped. please see `services.AddScoped(typeof(IGenericHttpClient<>), typeof(GenericHttpClient<>));` – Nb777 Dec 14 '21 at 10:32
  • Try to use `HttpClient` like [`services.AddHttpClient(c => c.BaseAddress = new System.Uri("https://api.github.com"));`](https://coderedirect.com/questions/514947/dependency-injection-httpclient-or-httpclientfactory). – Jason Pan Dec 15 '21 at 01:11
  • could you please try to explain to me? because you just said try to do like that ... . My main problem that I don't understand why blazor saved roles of first user after logged in for every next user. About using `services.AddHttpClien`, I using `IHttpClientFactory` in `IGenericHttpClient` to add baseaddress – Nb777 Dec 15 '21 at 07:49