16

I'm trying to implement an IdentityServer that handles an SSO for a multitenant application. Our system will have only one IdentityServer4 instance to handle the authentication of a multitentant client.

On the client side, I'm using the acr_value to pass the tenant Id. A piece of code from the Startup.cs file is as follows:

public void ConfigureServices(IServiceCollection services)
{
        services.AddMvc();
        services.AddAuthorization();

        services.AddAuthentication(options =>
            {
                options.DefaultScheme = "Cookies";
                options.DefaultChallengeScheme = "oidc";
            })
            .AddCookie("Cookies")
            .AddOpenIdConnect("oidc", options =>
            {
                options.SignInScheme = "Cookies";
                options.Authority = "http://localhost:5000";
                options.RequireHttpsMetadata = false;
                options.ClientId = "Client1";
                options.ClientSecret = "secret";
                options.ResponseType = "code id_token";
                options.SaveTokens = true;
                options.GetClaimsFromUserInfoEndpoint = true;                    
                options.Scope.Add("openid");
                options.Scope.Add("profile");
                options.Scope.Add("offline_access");
                options.Events.OnRedirectToIdentityProvider = n =>
                {
                    if (n.ProtocolMessage.RequestType == 
                          OpenIdConnectRequestType.Authentication)
                    {
                        n.ProtocolMessage.AcrValues = "tenant:clientId1";
                    }
                    return Task.FromResult(0);
                };
            });
}

For the identity server the IdentityServer4 with ASP.NET Core Identity is used. To handle multitenant client authentication I followed the instructions given by Scott Brady for ASP.NET Identity in this post: https://www.scottbrady91.com/ASPNET-Identity/Quick-and-Easy-ASPNET-Identity-Multitenancy

I modified the UserStore to receive the tenant Id but the moment of the UserStore instance is injected for the AccountController I can't retrieve the passed acr_value.

Has any one faced this problem before?

Palle Due
  • 5,929
  • 4
  • 17
  • 32
  • Perhaps this article can help you: https://leastprivilege.com/2017/11/15/missing-claims-in-the-asp-net-core-2-openid-connect-handler/ –  Jan 23 '18 at 18:08
  • Thanks for the advice. This article is to pass the claim acr from the endpoint to a client. I'm doing the inverse, passing the acr to the Server endpoint and verifying that user exists for this acr tenant value – Francisco Vera Voronisky Jan 23 '18 at 22:13
  • Just wandering if you've made any progress on this. Are you trying to have different `.well-known/openid-configuration` per each tenant or just one? Also why do you need the `tenantId` in the `UserStore`, you can just create a custom `ApplicationSignInManager` that overrides `CanSignInAsync(ApplicationUser user)` and pass it in the `AccountController` `Login` like this: `var context = await _interaction.GetAuthorizationContextAsync(returnUrl);` `_signInManager.ClientId = context?.Tenant; ` However, how do you manage with this approach to sign out of one tenant and not the others? – Ovi Mar 02 '18 at 23:01
  • @Ovi exactly I'm guessing the issue will occur even when the user logs into other tenant and try to login to restricted tenant, because idsrv sso looks at the cookie and bypass the authentication. So the signout will also have an impact. I believe with this approach we need to create a tenant based cookie so that they are isolated at tenant level. – Jay Apr 05 '18 at 05:23
  • cool @Jay! Any sample on how you’d create the tenant based cookie in aspnetcore 2? – Ovi Apr 05 '18 at 20:09
  • @Ovi, thats just a theory I have at the moment, in the process to implement.. how did you go with your approach? any results to share? – Jay Apr 06 '18 at 03:21
  • @Jay, yes see my PR on Saaskit: https://github.com/saaskit/saaskit/pull/96 – Ovi Apr 06 '18 at 10:08

1 Answers1

13

if you haven't figure out yet, here is the solution

private readonly IIdentityServerInteractionService _interaction;


 var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl);
            var tenant = context.Tenant;
Jay
  • 1,869
  • 3
  • 25
  • 44
  • 9
    I've got no idea where to put that code. It is just code without any context. – Fred Oct 20 '20 at 14:30
  • 1
    @Fred thats because you have not looked at the basic code needed to make an IS4 server. `IIdentityServerInteractionService` is typically used in a controller... https://github.com/IdentityServer/IdentityServer4/blob/main/src/IdentityServer4/host/Quickstart/Account/AccountController.cs#L81 – Victorio Berra Feb 14 '21 at 19:20
  • @Fred the code can be used in any of the services, where you want to know the incoming request tenant Id. You can also use the HttpContext service to extract the request URL – Jay Mar 14 '21 at 22:44
  • ...well, on login page context.tenant is null... (using IdentityServer 6.1.5) To use the Identity Server in real multitenancy we need to know the tenant in every request, for example to brand the login page itself... – g.pickardou Sep 25 '22 at 09:48