Come accedere nuovamente all'utente in modo silenzioso utilizzando l'autenticazione IdentityServer4

Aug 20 2020

Abbiamo una soluzione basata su framework ABP di terze parti e la sua architettura multi-layer:

Stiamo usando Angular come web face e IdentityServer4 per l'autenticazione degli utenti. Quindi, stiamo eseguendo 2 host - host API HTTP e host IdentityServer e per quanto riguarda il web face - funziona in modo standard: casella di accesso, l'utente inserisce le credenziali - voilà.

Tuttavia, abbiamo una configurazione personalizzata che consente lo stesso nome di accesso con un tenant diverso. L'elenco degli inquilini viene visualizzato come un menu a discesa sull'interfaccia utente e vorremmo accedere nuovamente a un utente con l'inquilino selezionato anziché l'utente attualmente connesso. Deve apparire come una semplice pagina ricaricata. Il problema è che non ho una chiara comprensione di come implementarlo. Ho provato a utilizzare la seguente chiamata dal livello dell'applicazione, ma non funziona (l'errore è "Nessun gestore di autenticazione è registrato per lo schema 'Identity.Application' ...", ma non so come impostare configurazione dell'autenticazione a livello di applicazione per poter lavorare con il nostro 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
    }

Parte di configurazione dall'host API Http per l'autenticazione:

    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();

Risposte

1 Alexander Aug 26 2020 at 16:49

Missione compiuta. Passaggi:

  1. BACK-END: implementa e registra un validatore del tipo di concessione personalizzato nel progetto IdentityServer:

    SwitchToTenantGrantValidator: IdentityServer4.Validation.IExtensionGrantValidator

In breve, ValidateAsync accetta i dati dell'utente autenticato (il suo token di accesso, ID tenant, ecc.) E decide se l'utente deve essere lasciato entrare. Il metodo scrive i dati del tenant di destinazione nell'oggetto risultato del contesto;

  1. FRONT-END: effettua una chiamata a IdentityServer con il tipo di concessione personalizzato specificato, fornendo i dati richiesti per (1). Abbiamo utilizzato Angular, quindi ho dovuto estendere OAuthService per supportare la richiesta di tipo di concessione personalizzata;

  2. Metti tutto in ordine (se (2) ha avuto successo) per visualizzare i dati corretti nell'interfaccia utente: pulisci vecchi stati, ecc.