Tenant registreren en onboarding

Voorbeeldcode van

In dit artikel wordt beschreven hoe u een aanmeldingsproces implementeert in een multitenant-toepassing, waarmee een klant zich kan registreren voor uw toepassing.

Er zijn verschillende redenen om een aanmeldingsproces te implementeren:

  • Een AD-beheerder toestaan toestemming te geven dat de hele organisatie van de klant de toepassing gebruikt.
  • Creditcardbetaling of andere klantgegevens verzamelen.
  • Voer een een keer de installatie per tenant uit die nodig is voor uw toepassing.

Voor verificatie met Azure AD moet een toepassing toegang hebben tot de directory van de gebruiker. De toepassing heeft minimaal toestemming nodig om het gebruikersprofiel te lezen. De eerste keer dat een gebruiker zich meldt, toont Azure AD een toestemmingspagina met de machtigingen die worden aangevraagd. Door te klikken op Accepteren verleent de gebruiker toestemming aan de toepassing.

Standaard wordt toestemming per gebruiker verleend. Elke gebruiker die zich meldt, ziet de toestemmingspagina. Azure AD biedt echter ook ondersteuning voor beheerdersmachtiging, waardoor een AD-beheerder toestemming kan geven voor een hele organisatie.

Wanneer de toestemmingsstroom van de beheerder wordt gebruikt, wordt op de toestemmingspagina weergegeven dat de AD-beheerder namens de hele tenant toestemming verleent:

Toestemmingsprompt van beheerder

Nadat de beheerder op Accepteren heeft geklikt, kunnen andere gebruikers binnen dezelfde tenant zich aanmelden en slaat Azure AD het toestemmingsscherm over.

Alleen een AD-beheerder kan beheerders toestemming geven, omdat deze namens de hele organisatie toestemming verleent. Als een niet-beheerder probeert te verifiëren met de toestemmingsstroom van de beheerder, wordt in Azure AD een fout weergegeven:

Toestemmingsfout

Als de toepassing op een later moment aanvullende machtigingen nodig heeft, moet de klant zich opnieuw registreren en toestemming geven voor de bijgewerkte machtigingen.

Tenant-aanmelding implementeren

Voor de toepassing Tailspin Surveys hebben we verschillende vereisten gedefinieerd voor het aanmeldingsproces:

  • Een tenant moet zich registreren voordat gebruikers zich kunnen aanmelden.
  • Voor de aanmelding wordt gebruikgemaakt van de toestemmingsstroom van de beheerder.
  • Met registreren wordt de tenant van de gebruiker toegevoegd aan de toepassingsdatabase.
  • Nadat een tenant zich heeft aanmelding, toont de toepassing een onboardingpagina.

In deze sectie doorlopen we de implementatie van het aanmeldingsproces. Het is belangrijk om te begrijpen dat 'registreren' versus 'aanmelden' een toepassingsconcept is. Tijdens de verificatiestroom weet Azure AD niet van zichzelf of de gebruiker bezig is met het registreren. Het is aan de toepassing om de context bij te houden.

Wanneer een anonieme gebruiker de toepassing Surveys bezoekt, krijgt de gebruiker twee knoppen te zien, één om zich aan te melden en één om uw bedrijf in te schrijven (registreren).

Aanmeldingspagina voor toepassingen

Met deze knoppen worden acties in de klasse AccountController aanroepen.

De SignIn actie retourneert een ChallengeResult, waardoor de OpenID-Verbinding maken middleware wordt omgeleid naar het verificatie-eindpunt. Dit is de standaardmethode voor het activeren van verificatie in ASP.NET Core.

[AllowAnonymous]
public IActionResult SignIn()
{
    return new ChallengeResult(
        OpenIdConnectDefaults.AuthenticationScheme,
        new AuthenticationProperties
        {
            IsPersistent = true,
            RedirectUri = Url.Action("SignInCallback", "Account")
        });
}

Vergelijk nu de SignUp actie:

[AllowAnonymous]
public IActionResult SignUp()
{
    var state = new Dictionary<string, string> { { "signup", "true" }};
    return new ChallengeResult(
        OpenIdConnectDefaults.AuthenticationScheme,
        new AuthenticationProperties(state)
        {
            RedirectUri = Url.Action(nameof(SignUpCallback), "Account")
        });
}

Net SignIn als SignUp retourneert de actie ook een ChallengeResult . Maar deze keer voegen we een stukje statusinformatie toe aan de AuthenticationProperties in de ChallengeResult :

  • signup: een Booleaanse vlag die aangeeft dat de gebruiker het aanmeldingsproces heeft gestart.

De statusinformatie in AuthenticationProperties wordt toegevoegd aan de parameter OpenID Verbinding maken [status,] die retournaties tijdens de verificatiestroom afrondt.

Statusparameter

Nadat de gebruiker zich heeft geverifieerd in Azure AD en wordt teruggeleid naar de toepassing, bevat het verificatieticket de status. We gebruiken dit feit om ervoor te zorgen dat de waarde voor 'registreren' voor de hele verificatiestroom blijft bestaan.

In Azure AD wordt de beheerders toestemmingsstroom geactiveerd door een parameter 'prompt' toe te voegen aan de queryreeks in de verificatieaanvraag:

/authorize?prompt=admin_consent&...

De toepassing Surveys voegt de prompt toe tijdens de RedirectToIdentityProvider gebeurtenis. Deze gebeurtenis wordt aangeroepen voordat de middleware wordt omgeleid naar het verificatie-eindpunt.

public override Task RedirectToIdentityProvider(RedirectContext context)
{
    if (context.IsSigningUp())
    {
        context.ProtocolMessage.Prompt = "admin_consent";
    }

    _logger.RedirectToIdentityProvider();
    return Task.FromResult(0);
}

Instelling ProtocolMessage.Prompt geeft de middleware de opdracht om de parameter 'prompt' toe te voegen aan de verificatieaanvraag.

Houd er rekening mee dat de prompt alleen nodig is tijdens de aanmelding. Reguliere aanmelding mag hier niet in zijn op te nemen. Om er onderscheid tussen te maken, controleren we op signup de waarde in de verificatietoestand. Met de volgende extensiemethode wordt op deze voorwaarde gecontroleerd:

internal static bool IsSigningUp(this AuthenticationProperties properties)
{
    Guard.ArgumentNotNull(properties, nameof(properties));

    string signupValue = string.Empty;
    // Check the HTTP context and convert to string
    if ((properties == null) ||
        (!properties.Items.TryGetValue("signup", out signupValue)))
    {
        return false;
    }

    // We have found the value, so see if it's valid
    bool isSigningUp;
    if (!bool.TryParse(signupValue, out isSigningUp))
    {
        // The value for signup is not a valid boolean, throw

        throw new InvalidOperationException($"'{signupValue}' is an invalid boolean value");
    }

    return isSigningUp;
}

Een tenant registreren

In de toepassing Surveys wordt informatie over elke tenant en gebruiker opgeslagen in de toepassingsdatabase.

Tenanttabel

In de tabel Tenant is IssuerValue de waarde van de vergeverclaim voor de tenant. Voor Azure AD biedt dit https://sts.windows.net/<tentantID> een unieke waarde per tenant.

Wanneer een nieuwe tenant zich meldt, schrijft de toepassing Surveys een tenantrecord naar de database. Dit gebeurt binnen de AuthenticationValidated gebeurtenis. (Doe dit niet vóór deze gebeurtenis, omdat het id-token nog niet wordt gevalideerd, zodat u de claimwaarden niet kunt vertrouwen. Zie [Verificatie.]

Hier is de relevante code van de toepassing Surveys:

public override async Task TokenValidated(TokenValidatedContext context)
{
    var principal = context.Principal;
    var userId = principal.GetObjectIdentifierValue();
    var tenantManager = context.HttpContext.RequestServices.GetService<TenantManager>();
    var userManager = context.HttpContext.RequestServices.GetService<UserManager>();
    var issuerValue = context.SecurityToken.Issuer;
    _logger.AuthenticationValidated(userId, issuerValue);

    // Normalize the claims first.
    NormalizeClaims(principal);
    var tenant = await tenantManager.FindByIssuerValueAsync(issuerValue);

    if (context.Properties.IsSigningUp())
    {
        if (tenant == null)
        {
            tenant = await SignUpTenantAsync(context, tenantManager)
                .ConfigureAwait(false);
        }

        // In this case, we need to go ahead and set up the user signing us up.
        await CreateOrUpdateUserAsync(context.Ticket, userManager, tenant)
            .ConfigureAwait(false);
    }
    else
    {
        if (tenant == null)
        {
            _logger.UnregisteredUserSignInAttempted(userId, issuerValue);
            throw new SecurityTokenValidationException($"Tenant {issuerValue} is not registered");
        }

        await CreateOrUpdateUserAsync(context.Principal, userManager, tenant)
            .ConfigureAwait(false);
    }
}

Deze code doet het volgende:

  1. Controleer of de waarde van de vergever van de tenant al in de database staat. Als de tenant zich niet heeft aangemeld, wordt FindByIssuerValueAsync null geretourneerd.
  2. Als de gebruiker zich aanmeldt:
    1. Voeg de tenant toe aan de database ( SignUpTenantAsync ).
    2. Voeg de geverifieerde gebruiker toe aan de database ( CreateOrUpdateUserAsync ).
  3. Voltooi anders de normale aanmeldingsstroom:
    1. Als de vergever van de tenant niet in de database is gevonden, betekent dit dat de tenant niet is geregistreerd en de klant zich moet registreren. In dat geval geeft u een uitzondering op om ervoor te zorgen dat de verificatie mislukt.
    2. Maak anders een databaserecord voor deze gebruiker als er nog geen is ( CreateOrUpdateUserAsync ).

Dit is de SignUpTenantAsync methode waarmee de tenant aan de database wordt toegevoegd.

private async Task<Tenant> SignUpTenantAsync(TokenValidatedContext context, TenantManager tenantManager)
{
    Guard.ArgumentNotNull(context, nameof(context));
    Guard.ArgumentNotNull(tenantManager, nameof(tenantManager));

    var principal = context.Principal;
    var issuerValue = principal.GetIssuerValue();
    var tenant = new Tenant
    {
        IssuerValue = issuerValue,
        Created = DateTimeOffset.UtcNow
    };

    try
    {
        await tenantManager.CreateAsync(tenant)
            .ConfigureAwait(false);
    }
    catch(Exception ex)
    {
        _logger.SignUpTenantFailed(principal.GetObjectIdentifierValue(), issuerValue, ex);
        throw;
    }

    return tenant;
}

Hier volgt een samenvatting van de volledige aanmeldingsstroom in de toepassing Surveys:

  1. De gebruiker klikt op de knop Registreren.
  2. De AccountController.SignUp actie retourneert een resultaat van een uitdaging. De verificatiestatus bevat de waarde 'registreren'.
  3. Voeg in RedirectToAuthenticationEndpoint de gebeurtenis de prompt admin_consent toe.
  4. De OpenID Verbinding maken middleware omgeleid naar Azure AD en de gebruiker wordt geverifieerd.
  5. Zoek in AuthenticationValidated de gebeurtenis naar de status 'registreren'.
  6. Voeg de tenant toe aan de database.

Volgende