How to re-login user silently using IdentityServer4 authentication
Asked Answered
H

1

0

We have solution based on 3rd party ABP framework and its multi-layer architecture: Layers interaction

We are using Angular as a web face and IdentityServer4 for user authentication. So, we are running 2 hosts - HTTP API host and IdentityServer host and as to web face - it works in a standard way: login box, user enters credentials - voila.

Though, we have a custom setup allowing the same login name under different tenant. The tenants list is displayed as a dropdown on UI and we would like to re-login a user with the selected tenant instead of the user currently logged in. It needs to look like a simple page reload. The problem is I don't have clear understanding how to implement this. I've tried to use the following call from application layer, but it does not work (error is "No authentication handler is registered for the scheme 'Identity.Application'...", but I don't know how to set up authentication configuration on application layer to be able to work with our IdentityServer):

    [HttpGet]
    public async Task<TenantDto> SwitchTenantForCurrentUser(Guid? tenantId)
    {
        var abxUser = await _abxUserRepository.FirstOrDefaultAsync(x => x.Login == CurrentUser.UserName && x.Tenant.AbpId == tenantId);

        if (abxUser == null)
            return null;

        using var _ = _abpCurrentTenant.Change(tenantId);

        var currentTenant = await _abxTenantRepository.FirstOrDefaultAsync(x => x.AbpId == _abpCurrentTenant.Id.Value);
        var identityUser = await _identityUserRepository.FindByNormalizedUserNameAsync(abxUser.Login.ToUpper());

        if (await _signInManager.CanSignInAsync(identityUser))
        {
            await _signInManager.SignOutAsync();
            await _signInManager.SignInAsync(identityUser, true);
        }

        return ObjectMapper.Map<Tenant, TenantDto>(currentTenant); // Not decided yet what to return, it depends on proper implementation
    }

Configuration part from Http API host as to authentication:

    private void ConfigureAuthentication(ServiceConfigurationContext context, IConfiguration configuration)
    {
        context.Services.AddAuthentication("Bearer")
            .AddIdentityServerAuthentication(options =>
            {
                options.Authority = configuration["AuthServer:Authority"];
                options.RequireHttpsMetadata = true;
                options.ApiName = "CentralTools";
                options.JwtBackChannelHandler = new HttpClientHandler
                {
                    //TODO: use valid certificate in future and change the logic
                    ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator
                };
            });

        context.Services.AddAbpIdentity().AddDefaultTokenProviders();
Heinrike answered 20/8, 2020 at 13:53 Comment(0)
H
1

Mission accomplished. Steps:

  1. BACK-END: implement & register custom grant type validator in IdentityServer project:

    SwitchToTenantGrantValidator : IdentityServer4.Validation.IExtensionGrantValidator

In short, ValidateAsync accepts the data of authenticated user (his access token, tenant ID, etc.) and makes the decision if the user has to be let in. The method writes data of target tenant into context result object;

  1. FRONT-END: make a call to IdentityServer with the given custom grant type, supplying data required for (1). We used Angular, so I had to extend OAuthService to support custom grant type request;

  2. Bring everything in order (if (2) was successful) to display correct data in UI: clean old states, etc.

Heinrike answered 26/8, 2020 at 16:49 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.