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

How to implement authorization in the dashboard module #1097

Closed
albertopm19 opened this issue Mar 2, 2022 · 7 comments
Closed

How to implement authorization in the dashboard module #1097

albertopm19 opened this issue Mar 2, 2022 · 7 comments

Comments

@albertopm19
Copy link
Contributor

Thanks for this great library.

We are implementing it in our microservices and we understand how to authenticate users with the dashboard module, but... How can we limit their access, that is, how can we say who can access it specifically?
In the example it only shows how to configure the authentication but not the authorization to limit access to specific users of a company....

Thank you very much!!

@yang-xiaodong
Copy link
Member

yang-xiaodong commented Mar 2, 2022

Our Dashboard does not display different views according to different identity roles, so as long as the authentication is passed, all views can be seen and operations can be performed.

You need to control the systems that the company's users can access in the OIDC server system, not here

@albertopm19
Copy link
Contributor Author

Thank you for this very fast response.

The problem is that we use the Duende identity server that is compatible with OpenID, but we only use it for authentication, not for authorization, so we always delegate the authorization to the microservices.

and we can't think of how to limit the use of the dashboard to a reduced set of company users who use our identity server.

@yang-xiaodong
Copy link
Member

yang-xiaodong commented Mar 2, 2022

Well, I thought maybe you could add some logic at the custom AuthenticationHandler to suit your needs.

https://github.com/dotnetcore/CAP/blob/master/samples/Sample.Dashboard.Auth/MyDashboardAuthenticationHandler.cs#L24

PS:

In our SSO system, there is a table to store the clients that the user can access. And override the FindClientByIdAsync in the IClientStore interface, when the method is called, it will get the user from the context and verify whether it has access rights.

  public class ClientStore : IClientStore
  {
      private readonly HttpContext _context;
      private readonly ILogger<ClientStore> _logger;
      private readonly IResourceManager _manager;
      private readonly IUserManager _userManager;

      public ClientStore(IResourceManager manager, IUserManager userManager, IHttpContextAccessor accessor,
          ILogger<ClientStore> logger)
      {
          _manager = manager;
          _userManager = userManager;
          _logger = logger;
          _context = accessor.HttpContext;
      }

      public async Task<Client> FindClientByIdAsync(string clientId)
      {
          //When an SSO user logs in to another system, check whether the user can log in to the target system
          if (_context.User.IsAuthenticated())
          {
              var userId = int.Parse(_context.User.GetSubjectId());
              if (_userManager.ClientValidate(userId, int.Parse(clientId)))
              {

                  if (await _userManager.CheckRequire2FAAsync(userId))
                  {
                      throw new DomainRedirectException("You need enable 2FA!","/Account/Require2FA");
                  }

                  _logger.LogDebug($"Logged-in User Find Client Success. Id: {clientId}, UserId: {userId} ");
                  return ConvertToClient(await _manager.GetClientAsync(ConvertClientIdToInt(clientId)));
              }

              _logger.LogDebug($"Find client failed. Razon: No authority. Id: {clientId}, UserId: {userId} ");
              throw new DomainException("You do not have access to this system", 401);
          }

          _logger.LogDebug($"Find client success, Id: {clientId}");
          return ConvertToClient(await _manager.GetClientAsync(ConvertClientIdToInt(clientId)));
      }

@albertopm19
Copy link
Contributor Author

Thank you very much for the suggestion, we will see if it can be applied to our SSO or we have to apply another alternative.

We will write down the final result in the incidence in case it can be useful for other companies

Once again, thank you very much for your work :)

@albertopm19
Copy link
Contributor Author

albertopm19 commented Mar 3, 2022

We are testing with the Auth example and our Identity server with this config:

    Startup.cs
	
   public void ConfigureServices(IServiceCollection services)
    {
        services
           .AddAuthorization()
           .AddAuthentication(options =>
           {
               options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
               options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
           })
           .AddCookie()
           .AddOpenIdConnect(options =>
           {
               options.Authority = "https://idsd.ifema.es/";
               options.ClientId = "AdminCAPPrueba";
               options.ClientSecret = "xxxxxxxxxx";
               options.ResponseType = "code";
               options.UsePkce = true;
               options.Scope.Clear();
               options.Scope.Add("openid");
               options.Scope.Add("profile");                   
           });

        services.AddCap(cap =>
        {
            cap.UseDashboard(d =>
            {
                d.UseChallengeOnAuth = true;
                d.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
                d.UseAuth = true;
                //d.DefaultAuthenticationScheme = "MyDashboardScheme";
            });
            cap.UseInMemoryStorage();
            cap.UseInMemoryMessageQueue();
        });

        services.AddControllers();
    }
	
	public void Configure(IApplicationBuilder app)
    {            
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        app.UseCookiePolicy();
        app.UseEndpoints(endpoints =>
        {
            endpoints.MapControllers();
        });
    }

But it does not work because the authentication is not being launched against our identity server.

If we change the code of

CAP.BuilderExtension.cs to this

    internal static async Task<bool> Authentication(HttpContext context, DashboardOptions options)
    {
        if (options.UseAuth)
        {
            var result = await context.AuthenticateAsync(options.DefaultAuthenticationScheme);

            if (result.Succeeded && result.Principal != null)
            {
                context.User = result.Principal;
            }
            // we have to comment these lines
            //else
            //{
            //    return false;
            //}
        }

        var isAuthenticated = context.User?.Identity?.IsAuthenticated;

        if (isAuthenticated == false && options.UseChallengeOnAuth)
        {
            await context.ChallengeAsync(options.DefaultChallengeScheme);
            await context.Response.CompleteAsync();

            return false;
        }

        return true;
    } 

now is working well.... Is this a bug or are we doing anything bad ?.

Thanks!!!

yang-xiaodong added a commit that referenced this issue Mar 3, 2022
@yang-xiaodong
Copy link
Member

yang-xiaodong commented Mar 3, 2022

Oh, I think It's a bug. Challenge should be executed first, I released a preview version to fix this in a few minutes ago.

You can test it with the version 6.1.0-preview-163077268.

// Set to false or remove this line if you does't needs custom authenticate.
 d.UseAuth = false; 

@albertopm19
Copy link
Contributor Author

albertopm19 commented Mar 3, 2022

ok, now the process is better understood.
We have tested the patch and it works.
Thank you very much!!
We continue with the authorization alternatives and we will add it to this incidence if it seems good to you....

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants