OpenID Connect authentication in ASP.NET Core: filter by tenant ID

  Kiến thức lập trình

I am developing an ASP.NET Core MVC application that uses Microsoft Entra ID for authentication. The application is designed to support multiple tenants, and I want to restrict access so that only users from specific tenants can log in. I have configured an AllowedTenants array in my appsettings.json file and am attempting to block users from tenants not included in this array. Despite this, my current implementation is not functioning as intended, and users from unauthorized tenants are still able to access the application.
Here is my setup:

Program.cs

public class Program
{
    public static void Main(string[] args)
    {
        var builder = WebApplication.CreateBuilder(args);

        builder.Services.AddControllersWithViews();

        var sqlConnectionString = builder.Configuration.GetConnectionString("DbContext");

        builder.Services.AddDbContext<MyAppContext>(options =>
            options.UseSqlServer(sqlConnectionString ?? throw new InvalidOperationException("Connection string not found.")));

        var allowedTenants = builder.Configuration.GetSection("AzureAd:AllowedTenants").Get<string[]>();

        builder.Services.AddAuthentication(OpenIdConnectDefaults.AuthenticationScheme)
            .AddMicrosoftIdentityWebApp(builder.Configuration.GetSection("AzureAd"))
        .EnableTokenAcquisitionToCallDownstreamApi(builder.Configuration.GetSection("DownstreamApis:MicrosoftGraph:Scopes").Get<string[]>())
            .AddMicrosoftGraph(builder.Configuration.GetSection("DownstreamApis:MicrosoftGraph"))
            .AddInMemoryTokenCaches();

        builder.Services.Configure<JwtBearerOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
        {
            var existingOnTokenValidatedHandler = options.Events.OnTokenValidated;
            options.Events.OnTokenValidated = async context =>
            {
                await existingOnTokenValidatedHandler(context);
                var tenantId = context.Principal?.FindFirst("tid")?.Value;
                if (!allowedTenants!.Contains(tenantId))
                {
                    throw new UnauthorizedAccessException("This tenant is not authorized");
                }
            };
        });

        builder.Services.AddAuthorization(options =>
        {
            options.FallbackPolicy = options.DefaultPolicy;
        });

        builder.Services.AddRazorPages(options =>
        {
        })
        .AddMvcOptions(options => { })
        .AddMicrosoftIdentityUI();

        builder.Services.AddScoped<IFormFileCheckerInterface, IFormFileChecker>();

        var app = builder.Build();

        if (!app.Environment.IsDevelopment())
        {
            app.UseExceptionHandler("/Home/Error");
            app.UseHsts();
        }

        app.UseHttpsRedirection();
        app.UseStaticFiles();

        app.UseRouting();

        app.UseAuthentication();
        app.UseAuthorization();

        app.MapRazorPages();

        app.MapControllerRoute(
            name: "default",
            pattern: "{controller=Home}/{action=Index}/{id?}");

        app.UseRewriter(
            new RewriteOptions().Add(
                context =>
                {
                    if (context.HttpContext.Request.Path == "/MicrosoftIdentity/Account/SignedOut")
                    { context.HttpContext.Response.Redirect("/Home/Index"); }
                })
        );

        app.Run();
    }
}

appsettings.json

{
  "AzureAd": {
    "Instance": "micrososft_instance",
    "Domain": "my_domain",
    "ClientId": "client_id",
    "ClientSecret": "client_secret",
    "TenantId": "organizations",
    "CallbackPath": "/signin-oidc",
    "AllowedTenants": [ "tenant_one", "tenant_two" ]
  },
  "DownstreamApis": {
    "MicrosoftGraph": {
      "BaseUrl": "https://graph.microsoft.com/v1.0",
      "Scopes": [ "User.Read", "User.ReadWrite.All" ]
    }
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning"
    }
  },
  "ConnectionStrings": {
    "DbContext": "db_context"
  },
  "AllowedHosts": "*"
}

Controllerfunction:

[HttpGet]
[Authorize]
public IActionResult Index()
{
    try
    {
        return View();
    }
    catch (Exception ex) 
    {
        _logger.LogError("An error occurred when trying to return Index page: " + ex.Message);
        return StatusCode(500, "An error occurred when trying to return Index page");
    }
}

What I am expecting:

I expected the application to throw an UnauthorizedAccessException and deny access to users from tenants not listed in the AllowedTenants array. However, users from unauthorized tenants are still able to access the application.

What could be wrong in my implementation, and how can I correctly restrict access based on tenant ID?

Theme wordpress giá rẻ Theme wordpress giá rẻ Thiết kế website

LEAVE A COMMENT