Registrering och registrering av klientorganisation

Exempelkod

Den här artikeln beskriver hur du implementerar en registreringsprocess i ett program för flera organisationer i samma organisation, vilket gör att en kund kan registrera sig för ditt program.

Det finns flera skäl att implementera en registreringsprocess:

  • Tillåt en AD-administratör att godkänna att kundens hela organisation använder programmet.
  • Samla in kreditkortsbetalning eller annan kundinformation.
  • Utför alla inställningar för en gång per klientorganisation som krävs av ditt program.

För att kunna autentisera med Azure AD behöver ett program åtkomst till användarens katalog. Programmet måste minst ha behörighet att läsa användarens profil. Första gången en användare loggar in visar Azure AD en sida för medgivande som visar de behörigheter som begärs. Genom att klickapå Acceptera ger användaren behörighet till programmet.

Som standard beviljas medgivande per användare. Alla användare som loggar in ser sidan för medgivande. Azure AD stöder dock även administratörsmedgivande,vilket gör att en AD-administratör kan godkänna en hel organisation.

När flödet för administratörsmedgivande används visar sidan för medgivande att AD-administratören beviljar behörighet åt hela klientorganisationen:

Fråga om administratörsmedgivande

När administratören klickar på Accepterakan andra användare i samma klientorganisation logga in, så hoppar Azure AD över skärmen för medgivande.

Endast en AD-administratör kan ge administratörsmedgivande, eftersom det ger behörighet åt hela organisationen. Om en icke-administratör försöker autentisera med flödet för administratörsmedgivande visar Azure AD ett fel:

Fel vid medgivande

Om programmet kräver ytterligare behörigheter vid ett senare tillfälle måste kunden registrera sig igen och samtycka till de uppdaterade behörigheterna.

Implementera registrering av klientorganisation

För Programmet Tailspin Surveys har vi definierat flera krav för registreringsprocessen:

  • En klientorganisation måste registrera sig innan användare kan logga in.
  • Registrering använder flödet för administratörsmedgivande.
  • Registrering lägger till användarens klientorganisation i programdatabasen.
  • När en klientorganisation har registrerar sig visar programmet en onboarding-sida.

I det här avsnittet går vi igenom implementeringen av registreringsprocessen. Det är viktigt att förstå att "registrera dig" jämfört med "logga in" är ett programkoncept. Under autentiseringsflödet vet Azure AD inte om användaren håller på att registrera sig. Det är upp till programmet att hålla reda på kontexten.

När en anonym användare besöker programmet Surveys visas två knappar för användaren, en för att logga in och en för att "registrera ditt företag" (registrera sig).

Sidan för registrering av program

Dessa knappar anropar åtgärder i AccountController klassen .

Åtgärden SignIn returnerar SignIn, vilket gör att OpenID-Anslut mellanprogram omdirigerar till autentiseringsslutpunkten. Det här är standardalternativet för att utlösa autentisering i ASP.NET Core.

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

Jämför nu SignUp åtgärden:

[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")
        });
}

Precis SignIn som SignUp returnerar åtgärden även en ChallengeResult . Men den här gången lägger vi till en delstatsinformation i AuthenticationProperties i ChallengeResult :

  • registrering: En boolesk flagga som anger att användaren har startat registreringsprocessen.

Tillståndsinformationen i AuthenticationProperties läggs till i OpenID-Anslut AuthenticationProperties som tur och retur under autentiseringsflödet.

Tillståndsparameter

När användaren autentiseras i Azure AD och omdirigeras tillbaka till programmet innehåller autentiseringsbiljetten tillståndet. Vi använder det här faktum för att se till att värdet för "registrering" finns kvar i hela autentiseringsflödet.

I Azure AD utlöses flödet för administratörsmedgivande genom att en "prompt"-parameter läggs till i frågesträngen i autentiseringsbegäran:

/authorize?prompt=admin_consent&...

Programmet Surveys lägger till prompten under RedirectToIdentityProvider händelsen. Den här händelsen anropas precis innan mellanprogram omdirigeras till autentiseringsslutpunkten.

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

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

Inställningen ProtocolMessage.Prompt uppmanar mellanprogram att lägga till parametern "prompt" i autentiseringsbegäran.

Observera att prompten endast behövs under registrering. Vanlig inloggning bör inte inkludera den. För att skilja mellan dem söker vi efter signup värdet i autentiseringstillståndet. Följande tilläggsmetod söker efter det här villkoret:

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;
}

Registrera en klientorganisation

Programmet Surveys lagrar viss information om varje klient och användare i programdatabasen.

Klienttabell

I tabellen Klientorganisation är IssuerValue värdet för utfärdaranspråk för klienten. För Azure AD är detta och https://sts.windows.net/<tentantID> ger ett unikt värde per klientorganisation.

När en ny klientorganisation registrerar sig skriver programmet Surveys en klientpost till databasen. Detta sker i AuthenticationValidated händelsen. (Gör det inte före den här händelsen eftersom ID-token inte verifieras ännu, så du kan inte lita på anspråksvärdena. Se Autentisering.

Här är relevant kod från programmet 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);
    }
}

Den här koden gör följande:

  1. Kontrollera om klientens utfärdarvärde redan finns i databasen. Om klienten inte har registrerat sig FindByIssuerValueAsync returnerar null.
  2. Om användaren registrerar sig:
    1. Lägg till klientorganisationen i databasen ( SignUpTenantAsync ).
    2. Lägg till den autentiserade användaren i databasen ( CreateOrUpdateUserAsync ).
  3. Annars slutför du det normala inloggningsflödet:
    1. Om klientens utfärdare inte hittades i databasen innebär det att klienten inte är registrerad och att kunden måste registrera sig. I så fall undantag som gör att autentiseringen misslyckas.
    2. Annars skapar du en databaspost för den här användaren, om det inte redan finns en ( CreateOrUpdateUserAsync ).

Här är metoden SignUpTenantAsync som lägger till klientorganisationen i databasen.

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;
}

Här är en sammanfattning av hela inloggningsflödet i programmet Surveys:

  1. Användaren klickar på knappen Registrera.
  2. Åtgärden AccountController.SignUp returnerar ett utmaningsresultat. Autentiseringstillståndet innehåller "signup"-värdet.
  3. I händelsen RedirectToAuthenticationEndpoint lägger du till admin_consent prompten.
  4. OpenID-Anslut mellanprogram omdirigeras till Azure AD och användaren autentiseras.
  5. I händelsen AuthenticationValidated letar du efter "signup"-statusen.
  6. Lägg till klientorganisationen i databasen.

Nästa