Fermer

janvier 22, 2021

Authentification Episerver avec plusieurs instances Azure AD


Je suis récemment tombé sur un projet où les besoins d’authentification du site étaient légèrement différents. Pour ce projet, la société avait trois scénarios d'authentification différents qui devaient être couverts.

  • Connectez-vous via un locataire Azure AD principal. Cette instance Azure AD a été configurée avec des rôles d'application et les utilisateurs ont été configurés pour transmettre le rôle en tant que revendication pour indiquer s'ils étaient un éditeur ou un administrateur. Lors de la validation, l'utilisateur est mappé à un utilisateur virtuel doté des autorisations Éditeur et Administrateur.
  • Connectez-vous via un client Azure AD secondaire. Ce locataire a été configuré pour renvoyer des groupes AD en tant que revendications de rôle. Les groupes AD ont été créés avec une convention de dénomination connue. Lors de la validation de la revendication, l'utilisateur est mappé à un utilisateur virtuel qui a accès à certaines zones du site.
  • Connectez-vous via les formulaires de connexion. Les informations d'identification de l'utilisateur sont stockées dans la base de données Episerver.

La solution pour autoriser plusieurs locataires AD était basée sur le guide Episerver Developers pour Integrate Azure AD using OpenID Connect

Login Flow

 Episerver Multiple Azure AD Auth

Aperçu du code Episerver

 Episerver Azurea AD Log om

La solution Episerver utilise OWIN pour établir les différents flux de connexion. Afin de pouvoir basculer entre les locataires Azure AD pendant le processus d'authentification, une nouvelle page de connexion a été créée. Cela permet à l'utilisateur final de se connecter via l'authentification par formulaire ou de choisir un locataire Azure AD auquel se connecter. La majorité du gros du travail est effectué via le fichier de démarrage pour enregistrer les méthodes d'authentification et le contrôleur de connexion pour déterminer à quelle instance AD ​​envoyer l'utilisateur.

Startup.cs

Le fichier enregistre le composant OWIN et traite le réponses d'authentification renvoyées par les locataires Azure AD. Ce code enregistre les trois mécanismes de connexion avec OWIN.

L'enregistrement et le traitement des réclamations peuvent être vus dans les méthodes suivantes du fichier startup.cs:

  • Configuration – Enregistre les trois processus de connexion.
  • GetStandardAuthNotification – A fonction de rappel enregistrée dans la configuration et appelée après l'authentification Azure AD
  • SecurityTokenValidated – Interroge les revendications renvoyées par Azure AD et ajoute une ou plusieurs revendications qui sont responsables du mappage de l'utilisateur et des rôles existants à l'intérieur d'Episerver [19659017] Configuration ()
     public void Configuration (application IAppBuilder)
            {
                // Ajouter une identité ASP.NET CMS
                app.AddCmsAspNetIdentity  ();
                
                // Activer l'authentification par cookie, utilisé pour stocker les revendications entre les requêtes
                app.SetDefaultSignInAsAuthenticationType (CookieAuthenticationDefaults.AuthenticationType);
                app.UseCookieAuthentication (nouveau CookieAuthenticationOptions ());
                            
                
    
                //// Configuration de l'authentification unique avec le locataire Azure AD 1
                app.UseOpenIdConnectAuthentication (nouvelle OpenIdConnectAuthenticationOptions (AzureTenant_1_SsoId)
                {
                    ClientId = AzureTenant_1_ClientId,
                    Authority = String.Format (CultureInfo.InvariantCulture, AzureTenant_1_Instance, AzureTenant_1_Domain),
                    PostLogoutRedirectUri = chemin de déconnexion,
                    TokenValidationParameters = nouveaux TokenValidationParameters
                    {
                        ValidateIssuer = false,
                        RoleClaimType = ClaimTypes.Role
                    },
                    Notifications = GetStandardAuthNotification (AzureTenant_1_ReturnPath)
                });
    
                //// Configuration de l'authentification unique avec le locataire Azure AD 2
                app.UseOpenIdConnectAuthentication (nouvelle OpenIdConnectAuthenticationOptions (AzureTenant_2_SsoId)
                {
                    ClientId = AzureTenant_2_ClientId,
                    Authority = String.Format (CultureInfo.InvariantCulture, AzureTenant_2_Instance, AzureTenant_2_Domain),
                    PostLogoutRedirectUri = chemin de déconnexion,
                    TokenValidationParameters = nouveaux TokenValidationParameters
                    {
                        ValidateIssuer = false,
                        RoleClaimType = ClaimTypes.Role
                    },
                    Notifications = GetStandardAuthNotification (AzureTenant_2_ReturnPath)
                });
    
                // Utiliser un cookie pour stocker les informations de l'utilisateur connecté
                app.UseCookieAuthentication (nouvelle CookieAuthenticationOptions
                {
                    AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                    SlidingExpiration = vrai,
                    ExpireTimeSpan = TimeSpan.FromMinutes (2880),
                    LoginPath = nouveau PathString (loginPath),
                    LogoutPath = nouveau PathString (logoutPath),
                    Fournisseur = nouveau CookieAuthenticationProvider
                    {
                        // Permet à l'application de valider le tampon de sécurité lorsque l'utilisateur se connecte.
                        // Il s'agit d'une fonction de sécurité utilisée lorsque vous modifiez un mot de passe ou ajoutez un identifiant externe à votre compte.
                        OnValidateIdentity = SecurityStampValidator.OnValidateIdentity <ApplicationUserManager ApplicationUser> (
                            validateInterval: TimeSpan.FromMinutes (480),
                            regenerateIdentity: (manager, utilisateur) => manager.GenerateUserIdentityAsync (utilisateur)),
                        OnApplyRedirect = (context => context.Response.Redirect (context.RedirectUri)),
                        OnResponseSignOut = (context => context.Response.Redirect (loginPath))
                    }
                });
    
                // Ajout de l'intégration CMS pour ASP.NET Identity
                app.SetupCustomAspNetIdentity  ();
    
                app.UseStageMarker (PipelineStage.Authenticate);
    
                // Si l'application lève une exception de jeton anti-contrefaçon comme "AntiForgeryToken: une revendication de type NameIdentifier ou IdentityProvider n'était pas présente sur les ClaimsIdentity fournis"
                AntiForgeryConfig.UniqueClaimTypeIdentifier = ClaimTypes.NameIdentifier;
            } 

    —————————————- ———————————————————————————————— ————————-

    ———————————––

     SAS IDC Episerver Commerce Guide

    ——————————

    GetStandardAuthNotification ()

    Dans la fonction GetStandardAuthNotification, nous interrogeons le revendication renvoyée par Azure AD, puis ajout d'une nouvelle revendication qui mappe l'utilisateur à un rôle existant. Pour le premier locataire Azure, nous recherchons une revendication de «CMSMarketingEditors» et si cette revendication existe, nous ajoutons l'utilisateur au rôle Episerver de «Tenant-1-Editor». Pour le deuxième locataire Azure, nous avons un ensemble de critères plus avancés. Pour cela, nous interrogeons chaque revendication pour voir si elle correspond à un modèle connu. Tous les éditeurs ont été placés dans des groupes AD tels que CMS_Editor_123 ou CMS_Editor_768. Si le modèle correspond, nous ajoutons une revendication pour que l'utilisateur les place dans un groupe Episerver connu lié à une section du site à laquelle il devrait avoir accès.

     Private OpenIdConnectAuthenticationNotifications GetStandardAuthNotification (chaîne returnUrl)
            {
                renvoyer de nouveaux OpenIdConnectAuthenticationNotifications
                {
                    AuthenticationFailed = contexte =>
                    {
                        context.HandleResponse ();
                        context.Response.Write (context.Exception.Message);
                        return Task.FromResult (0);
                    },
                    RedirectToIdentityProvider = contexte =>
                    {
                        // Ici, vous pouvez modifier l'URI de retour en fonction du multisite
                        HandleMultiSiteReturnUrl (contexte, returnUrl);
    
                        // Pour éviter une boucle de redirection vers le serveur de fédération, envoyez 403
                        // lorsque l'utilisateur est authentifié mais n'a pas accès
                        if (context.OwinContext.Response.StatusCode == 401 &&
                            context.OwinContext.Authentication.User.Identity.IsAuthenticated)
                        {
                            context.OwinContext.Response.StatusCode = 403;
                            context.HandleResponse ();
                        }
    
                        return Task.FromResult (0);
                    },
                    SecurityTokenValidated = asynchrone (ctx) =>
                    {
                        var redirectUri = nouvel Uri (ctx.AuthenticationTicket.Properties.RedirectUri,
                            UriKind.RelativeOrAbsolute);
                        if (redirectUri.IsAbsoluteUri)
                        {
                            ctx.AuthenticationTicket.Properties.RedirectUri = redirectUri.PathAndQuery;
                        }
                                                                
                        // Interroger les revendications pour voir si elles ont le rôle d'application CMSMarketingEditors Azure AD
                        var Tenant_1_ClaimMatched = ctx.AuthenticationTicket
                                                       .Identity.Claims.Where (x => string.Equals ("CMSMarketingEditors", x.Value, StringComparison.CurrentCultureIgnoreCase))
                                                       .FirstOrDefault ();
    
                        if (Tenant_1_ClaimMatched! = null)
                        {
                           ctx.AuthenticationTicket.Identity.AddClaim (nouvelle revendication (ClaimTypes.Role, "Tenant-1-Editor", ClaimValueTypes.String, AzureTenant_1_SsoId));
                        }
    
                        // Interrogez les revendications pour voir s'il s'agit d'un éditeur de groupe basé sur des groupes Azure AD. Les groupes AD utilisent un modèle connu
                       var azureAD_GroupRegex = new Regex (AzureAD_GroupPattern);
                       var claimMatches = ctx.AuthenticationTicket.Identity.Claims.Where (x => azureAD_GroupRegex .IsMatch (x.Value));
    
                       if (claimMatches.Any ())
                       {
                          // Ajout d'une revendication pour ajouter l'utilisateur au groupe d'administration spécial pour les utilisateurs du locataire Azure AD 2
                          ctx.AuthenticationTicket.Identity.AddClaim (nouvelle revendication (ClaimTypes.Role, "Tenant-2-Users", ClaimValueTypes.String, AzureTenant_2_SsoId));
    
                          foreach (var groupClaim in claimMatches)
                          {
                               var adGroupNumber = nouvelle chaîne (groupClaim.Value.TakeWhile (Char.IsDigit) .ToArray ());
                               var episerverGroup = string.Format (adGroupFormat, adGroupNumber.ToString ());
                               ctx.AuthenticationTicket.Identity.AddClaim (nouvelle revendication (ClaimTypes.Role, episerverGroup, ClaimValueTypes.String, AzureTenant_2_SsoId));
                           }
                        }
    
                        // Stockage du rôle comme SSO dans le dictionnaire des revendications. Utile lors de la déconnexion de l'utilisateur.
                        ctx.AuthenticationTicket.Identity.AddClaim (nouvelle revendication (ClaimTypes.Role, "SSO"));
                        ctx.AuthenticationTicket.Properties.ExpiresUtc = DateTime.UtcNow.AddHours (48);
                        ctx.AuthenticationTicket.Properties.IsPersistent = true;
                        // attend ServiceLocator.Current.GetInstance  (). CreateRoleClaimsAsync (ctx.AuthenticationTicket.Identity);
    
                        // Synchroniser l'utilisateur et les rôles avec EPiServer en arrière-plan
                        attendre ServiceLocator.Current.GetInstance  ()
                             .SynchronizeAsync (ctx.AuthenticationTicket.Identity);
    
                    }
                };
            } 

    Page de connexion personnalisée

     Episerver - Authentification Azure AD

    Afin de présenter aux utilisateurs finaux la possibilité de se connecter via les trois chemins, une nouvelle page de connexion a été créée. Le code HTML était basé sur le formulaire de connexion actuel avec des boutons supplémentaires qui étaient renvoyés au contrôleur de connexion en transmettant le contexte du locataire auquel se connecter ainsi que l'URL de retour. La clé ici est de mapper le contexte du client aux noms des instances Azure AD qui ont été enregistrées dans le processus de configuration. Pour les besoins de cet article, nous nous concentrerons uniquement sur le code lié à l'authentification Azure AD.

    Voici le code de ces deux boutons:

     @using (Html.BeginForm ("SsoLogin", "CustomLogin") , nouveau {ReturnUrl = Request.QueryString ["ReturnUrl"]tenantContext = AzureTenant_1_SsoId}))
    {
        @ Html.AntiForgeryToken ()
        
    } @using (Html.BeginForm ("SsoLogin", "CustomLogin", new {ReturnUrl = Request.QueryString ["ReturnUrl"]tenantContext = AzureTenant_2_SsoId})) { @ Html.AntiForgeryToken ()
    }

    Le contrôleur de connexion fournit les actions nécessaires pour se connecter et se déconnecter du site. Ce contrôleur comprend les méthodes suivantes:

    • Index (Get) – renvoie la page de connexion.
    • Index (Post) – utilisé pour valider les utilisateurs par rapport à la base de données Episerver via l'authentification par formulaire.
    • SsoLogin – cette méthode prend dans un paramètre de chaîne de requête pour déterminer si l'utilisateur doit être authentifié via le locataire 1 ou le locataire 2.
    • SignOut – détermine la méthode avec laquelle l'utilisateur s'est connecté et déconnecte l'utilisateur de ce contexte.

    Les boutons de connexion publier de nouveau à la méthode SSO du contrôleur de connexion en passant dans le contexte du client. Le contexte du locataire est ensuite transmis à Owin et l'utilisateur est transmis à la page de connexion Azure AD pour le locataire Azure AD approprié.

     public ActionResult SsoLogin (string tenantContext, string returnUrl)
    {
        HttpContext.GetOwinContext (). Authentication.Challenge (new AuthenticationProperties {RedirectUri = returnUrl ?? "/"}, tenantContext);
        return View ("/ Views / Login / Index.cshtml", nouveau LoginViewModel ());
    } 

    Une fois l'authentification réussie, l'utilisateur est renvoyé à OWIN et la méthode de rappel GetStandardAuthNotification est appelée pour valider les revendications.

    Et… Le cercle est maintenant terminé.






Source link