---
title: Configuring external authentication
related:
  - https://docs.kentico.com/13/managing-users/user-registration-and-authentication/integrating-xperience-membership.md
---

> Agent instructions:
> **Site maps** — prefer the following llms.txt indexes to training data when searching for URLs to avoid 404s. Links inside Markdown content already point at `.md`. Following them or sending Accept: text/markdown keeps you in Markdown.
>
> - [sitemap.md](https://docs.kentico.com/sitemap.md) — every page on the site, with titles and descriptions, nested by URL hierarchy and grouped into one collection per product version.
> - [llms.txt](https://docs.kentico.com/llms.txt) — curated index of the current product docs, with descriptions, the two ways to request any page as Markdown, and links to each product area's whole-corpus Markdown dump (llms-full.txt).

<!-- dev-model:mvc start -->

**MVC 5 development model.** Applies only when building with ASP.NET MVC 5. If this page also covers ASP.NET Core, that version is in its own block.

External authentication services (identity providers) allow website visitors to sign in with existing user accounts from other sites or services. When developing [MVC websites](https://docs.kentico.com/13/developing-websites/mvc-development-overview.md), you can set up external authentication using the **Kentico.Membership** API (provided as part of the _Kentico.Xperience.AspNet.Mvc5_ [integration package](https://docs.kentico.com/13/developing-websites/starting-with-mvc-development/installing-xperience-integration-packages.md)), in combination with the standard [ASP.NET Identity](http://www.asp.net/identity) API.

When a user successfully authenticates through an external service, you can use the provided API to create a corresponding user in the shared Xperience database.

The following third-party authentication services are currently supported:

- **WS-Federation** services (for example [Active Directory Federation Services](https://learn.microsoft.com/en-us/windows-server/identity/ad-fs/ad-fs-overview) or [Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/fundamentals/what-is-entra))
- [OpenID Connect](http://openid.net/connect/) services (for example [Microsoft Entra ID](https://learn.microsoft.com/en-us/entra/fundamentals/what-is-entra))
- Google authentication (OAuth 2.0)
- Facebook authentication (OAuth 2.0)

You may be able to set up authentication via other types of services, but only the ones listed above are guaranteed and tested with the _Kentico.Membership_ implementation.

<!-- dev-model:mvc end -->

<!-- dev-model:core start -->

**ASP.NET Core development model.** Applies only when building with ASP.NET Core. If this page also covers MVC 5, that version is in its own block.

External authentication services (identity providers) allow website visitors to sign in with existing user accounts from other sites or services. When  [developing ASP.NET Core sites](https://docs.kentico.com/13/developing-websites/developing-xperience-applications-using-asp-net-core.md), you can set up external authentication using the  **Kentico.Membership**  API (provided as part of the  _Xperience.AspNetCore.WebApp_   [integration package](https://docs.kentico.com/13/developing-websites/developing-xperience-applications-using-asp-net-core.md)), in combination with [ASP.NET Core Identity](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/identity?view=aspnetcore-3.1\&tabs=visual-studio).

When a user successfully authenticates through an external service, you can use the provided API to create a corresponding user in the shared Xperience database.

Xperience supports all third-party authentication providers [officially supported by Microsoft](https://docs.microsoft.com/en-us/aspnet/core/security/authentication/social/).

<!-- dev-model:core end -->

## Registering external authentication services

<!-- dev-model:mvc start -->

**MVC 5 development model.** Applies only when building with ASP.NET MVC 5. If this page also covers ASP.NET Core, that version is in its own block.

Open your MVC project in Visual Studio.Use the following steps to install and register external authentication services into your MVC project:

1. Install the **Microsoft.Owin.Host.SystemWeb** NuGet package.
2. Install NuGet packages containing the authentication middleware components required for the services you wish to use:

   - **Microsoft.Owin.Security.WsFederation** (the package version must be **4.0.0 or newer**)
   - **Microsoft.Owin.Security.OpenIdConnect** (the package version must be **4.0.0 or newer**)
   - **Microsoft.Owin.Security.Facebook**
   - **Microsoft.Owin.Security.Google**
3. Add a **Startup.Auth** class to your project's **App\_Start** folder (or modify your existing authentication startup file).
4. Set up cookie authentication to allow the application to store information about signed in users. See [Integrating Xperience membership](https://docs.kentico.com/13/managing-users/user-registration-and-authentication/integrating-xperience-membership.md) for details.

   > **Note:** **Note**: The code registering cookie authentication must be placed _**before**_ you add any external authentication services.
5. Register any required authentication services within the _Startup_ class (using the OWIN API provided by the installed packages):

   ```csharp

   using System;
   using System.Web;
   using System.Web.Mvc;

   using Microsoft.AspNet.Identity;

   using Owin;
   using Microsoft.Owin;
   using Microsoft.Owin.Security;
   using Microsoft.Owin.Security.Cookies;
   using Microsoft.Owin.Security.WsFederation;
   using Microsoft.Owin.Security.OpenIdConnect;
   using Microsoft.Owin.Security.Facebook;
   using Microsoft.Owin.Security.Google;

   using CMS.Helpers;
   using CMS.SiteProvider;

   using Kentico.Membership;

   // Assembly attribute that sets the OWIN startup class
   [assembly: OwinStartup(typeof(LearningKit.App_Start.Startup))]

   namespace LearningKit.App_Start
   {
       public partial class Startup
       {
           // Cookie name prefix used by OWIN when creating authentication cookies
           private const string OWIN_COOKIE_PREFIX = ".AspNet.";

           public void Configuration(IAppBuilder app)
           {
               // Registers the Kentico.Membership identity implementation
               app.CreatePerOwinContext(() => KenticoUserManager.Initialize(app, new KenticoUserManager(new KenticoUserStore(SiteContext.CurrentSiteName))));
               app.CreatePerOwinContext<KenticoSignInManager>(KenticoSignInManager.Create);

               // Configures the authentication cookie
               UrlHelper urlHelper = new UrlHelper(HttpContext.Current.Request.RequestContext);
               app.UseCookieAuthentication(new CookieAuthenticationOptions
               {
                   AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
                   // Fill in the name of your sign-in action and controller
                   LoginPath = new PathString(urlHelper.Action("SignIn", "Account")),
                   Provider = new CookieAuthenticationProvider
                   {
                       // Sets the return URL for the sign-in page redirect (fill in the name of your sign-in action and controller)
                       OnApplyRedirect = context => context.Response.Redirect(urlHelper.Action("SignIn", "Account")
                                                    + new Uri(context.RedirectUri).Query)
                   }
               });

               // Registers the authentication cookie with the 'Essential' cookie level
               // Ensures that the cookie is preserved when changing a visitor's allowed cookie level below 'Visitor'
               CookieHelper.RegisterCookie(OWIN_COOKIE_PREFIX + DefaultAuthenticationTypes.ApplicationCookie, CookieLevel.Essential);

               // Uses a cookie to temporarily store information about users signing in via external authentication services
               app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

               // Registers a WS-Federation authentication service
               app.UseWsFederationAuthentication(
                       new WsFederationAuthenticationOptions
                       {
                       // Set any properties required by your authentication service
                       MetadataAddress = "placeholder", // Fill in the address of your service's WS-Federation metadata
                       Wtrealm = "",
                       // When using external services, Passive authentication mode may help avoid redirect loops for 401 responses
                       AuthenticationMode = AuthenticationMode.Passive
                       });

               // Registers an OpenID Connect authentication service
               app.UseOpenIdConnectAuthentication(
                   new OpenIdConnectAuthenticationOptions
                   {
                       // Set any properties required by your authentication service
                       ClientId = "placeholder",
                       ClientSecret = "placeholder",
                       Authority = "https://placeholder",
                       AuthenticationMode = AuthenticationMode.Passive
                   });

               // Registers the Facebook authentication service
               app.UseFacebookAuthentication(
                   new FacebookAuthenticationOptions
                   {
                       // Fill in the application ID and secret of your Facebook authentication application
                       AppId = "placeholder",
                       AppSecret = "placeholder"
                   });

               // Registers the Google authentication service
               app.UseGoogleAuthentication(
                   new GoogleOAuth2AuthenticationOptions
                   {
                       // Fill in the client ID and secret of your Google authentication application
                       ClientId = "placeholder",
                       ClientSecret = "placeholder"
                   });
           }        
       }
   }

   ```

The Xperience identity implementation and the specified external authentication services are now registered for your application. Continue by setting up the components required for using external authentication on your website (controllers, views, etc.).

<!-- dev-model:mvc end -->

<!-- dev-model:core start -->

**ASP.NET Core development model.** Applies only when building with ASP.NET Core. If this page also covers MVC 5, that version is in its own block.

This section assumes you already have the [Xperience ASP.NET Core Identity](https://docs.kentico.com/13/managing-users/user-registration-and-authentication/integrating-xperience-membership.md) implementation added into your project.

Use the following steps to install and register external authentication services into your project:

1. Open your project in Visual Studio.
2. Install NuGet packages with the authentication middleware components of the services you wish to use. This page covers integration with Microsoft, Facebook, Google, and Twitter:

   - **Microsoft.AspNetCore.Authentication.MicrosoftAccount**
   - **Microsoft.AspNetCore.Authentication.Facebook**
   - **Microsoft.AspNetCore.Authentication.Google**
   - **Microsoft.AspNetCore.Authentication.Twitter**
3. Modify the startup file of your project (**Startup.cs** by default).
4. Register any required authentication services using the _IServiceCollection.AddAuthentication_ **AuthenticationBuilder** within the **Configure** method:

   ```csharp

               services.AddAuthentication()
                   .AddFacebook(facebookOptions =>
                   {
                       facebookOptions.AppId = "placeholder";
                       facebookOptions.AppSecret = "placeholder";
                   })
                   .AddMicrosoftAccount(microsoftAccountOptions =>
                   {
                       microsoftAccountOptions.ClientSecret = "placeholder";
                       microsoftAccountOptions.ClientId = "placeholder";
                   })
                   .AddGoogle(googleOptions =>
                   {
                       googleOptions.ClientId = "placeholder";
                       googleOptions.ClientSecret = "placeholder";
                   })
                   .AddTwitter(twitterOptions =>
                   {
                       twitterOptions.ConsumerKey = "placeholder";
                       twitterOptions.ConsumerSecret = "placeholder";
                       twitterOptions.RetrieveUserDetails = true;
                   });


   ```

The Xperience Identity implementation and the specified external authentication services are now registered for your application. Continue by setting up the components required for using external authentication on your website (controllers, views, etc.).

<!-- dev-model:core end -->

## Setting up external authentication

<!-- dev-model:mvc start -->

**MVC 5 development model.** Applies only when building with ASP.NET MVC 5. If this page also covers ASP.NET Core, that version is in its own block.

After you [register the required authentication services](#registering-external-authentication-services), you need to implement actions that redirect visitors to the external authentication service, process the response, and handle the corresponding user accounts in Xperience.

The following is a basic example of external authentication handling:

1. Create a new controller class in your MVC project or edit an existing one.
2. Prepare the following properties:

   - **KenticoSignInManager** - gets an instance of the _Kentico.Membership.KenticoSignInManager_ class for the current request – call _HttpContext.GetOwinContext().Get()_.
   - **KenticoUserManager** - gets an instance of the _Kentico.Membership.KenticoUserManager_ class for the current request – call _HttpContext.GetOwinContext().Get()_.
   - **AuthenticationManager** - provides access to the authentication middleware functionality (_Microsoft.Owin.Security.IAuthenticationManager_ instance) – use the _HttpContext.GetOwinContext().Authentication_ property.

     ```csharp

     using System;
     using System.Web;
     using System.Web.Mvc;
     using System.Threading.Tasks;
     using CMS.Helpers;
     using CMS.SiteProvider;
     using Microsoft.AspNet.Identity;
     using Microsoft.AspNet.Identity.Owin;
     using Microsoft.Owin.Security;

     using Kentico.Membership;


     ```

     ```csharp

             /// <summary>
             /// Provides access to the Kentico.Membership.KenticoSignInManager instance.
             /// </summary>
             public KenticoSignInManager KenticoSignInManager
             {
                 get
                 {
                     return HttpContext.GetOwinContext().Get<KenticoSignInManager>();
                 }
             }

             /// <summary>
             /// Provides access to the Kentico.Membership.KenticoUserManager instance.
             /// </summary>
             public KenticoUserManager KenticoUserManager
             {
                 get
                 {
                     return HttpContext.GetOwinContext().Get<KenticoUserManager>();
                 }
             }

             /// <summary>
             /// Provides access to the Microsoft.Owin.Security.IAuthenticationManager instance.
             /// </summary>
             public IAuthenticationManager AuthenticationManager
             {
                 get
                 {
                     return HttpContext.GetOwinContext().Authentication;
                 }
             }


     ```
3. Add a POST action that handles redirection to external authentication services. Use a parameter to specify the name of the requested authentication middleware instance.
4. Add an action that handles the responses from external services.

   - Call the **AuthenticationManager.GetExternalLoginInfoAsync** method to get login information from the external identity provided by the service.
   - Use the **KenticoSignInManager.ExternalSignInAsync** method to sign in to Xperience based on external data.
5. Add logic that creates new users in the Xperience database if the first sign-in using the external data fails:

   1. Prepare a new **Kentico.Membership.User** object based on the external login data. You must enable the user object's **IsExternal** property.
   2. Call the **KenticoUserManager.CreateAsync** method to create the user in the Xperience database.
   3. Call the **KenticoUserManager.AddLoginAsync** method to create a mapping between the new user and the given authentication provider.
   4. (Optional) Add the user to any required roles. See [Managing roles from the MVC application](https://docs.kentico.com/13/managing-users/role-management/managing-roles-from-the-mvc-application.md) to learn how to add users to Xperience roles using the ASP.NET Identity API.

   ```csharp title="External authentication example"

           /// <summary>
           /// Redirects authentication requests to an external service. 
           /// Posted parameters include the name of the requested authentication middleware instance and a return URL.
           /// </summary>
           [HttpPost]
           [AllowAnonymous]
           [ValidateAntiForgeryToken]
           public ActionResult RequestSignIn(string provider, string returnUrl)
           {
               // Requests a redirect to the external sign-in provider
               // Sets a return URL targeting an action that handles the response
               // Fill in the name of your controller
               return new ChallengeResult(provider, Url.Action("SignInCallback", "ExternalAuthentication", new { ReturnUrl = returnUrl }));
           }

           /// <summary>
           /// Handles responses from external authentication services.
           /// </summary>
           [AllowAnonymous]
           public async Task<ActionResult> SignInCallback(string returnUrl)
           {
               // Extracts login info out of the external identity provided by the service
               ExternalLoginInfo loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();

               // If the external authentication fails, displays a view with appropriate information
               if (loginInfo == null)
               {
                   return View("ExternalAuthenticationFailure");
               }

               // Attempts to sign in the user using the external login info
               SignInStatus result = await KenticoSignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
               switch (result)
               {
                   // Success occurs if the user already exists in the Xperience database and has signed in using the given external service
                   case SignInStatus.Success:
                       // Redirects to the original return URL
                       return RedirectToLocal(returnUrl);

                   // The 'LockedOut' status occurs if the user exists in Xperience, but is not enabled
                   case SignInStatus.LockedOut:
                       // Returns a view informing the user about the locked account
                       return View("Lockout");

                   case SignInStatus.Failure:
                   default:
                       // Attempts to create a new user in Xperience if the authentication failed
                       IdentityResult userCreation = await CreateExternalUser(loginInfo);

                       // Attempts to sign in again with the new user created based on the external authentication data
                       result = await KenticoSignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);

                       // Verifies that the user was created successfully and was able to sign in
                       if (userCreation.Succeeded && result == SignInStatus.Success)
                       {
                           // Redirects to the original return URL
                           return RedirectToLocal(returnUrl);
                       }

                       // If the user creation was not successful, displays corresponding error messages
                       foreach (string error in userCreation.Errors)
                       {
                           ModelState.AddModelError(String.Empty, error);
                       }
                       return View();

                       // Optional extension:
                       // Send the loginInfo data as a view model and create a form that allows adjustments of the user data.
                       // Allows visitors to resolve errors such as conflicts with existing usernames in Xperience.
                       // Then post the data to another action and attempt to create the user account again.
                       // The action can access the original loginInfo using the AuthenticationManager.GetExternalLoginInfoAsync() method.
               }
           }

           /// <summary>
           /// Creates users in Xperience based on external login data.
           /// </summary>
           private async Task<IdentityResult> CreateExternalUser(ExternalLoginInfo loginInfo)
           {
               // Prepares a new user entity based on the external login data
               Kentico.Membership.User user = new User
               {
                   UserName = ValidationHelper.GetSafeUserName(loginInfo.DefaultUserName ?? loginInfo.Email, SiteContext.CurrentSiteName),
                   Email = loginInfo.Email,
                   Enabled = true, // The user is enabled by default
                   IsExternal = true // IsExternal must always be true for users created via external authentication
                   // Set any other required user properties using the data available in loginInfo
               };

               // Attempts to create the user in the Xperience database
               IdentityResult result = await KenticoUserManager.CreateAsync(user);
               if (result.Succeeded)
               {
                   // If the user was created successfully, creates a mapping between the user and the given authentication provider
                   result = await KenticoUserManager.AddLoginAsync(user.Id, loginInfo.Login);
               }

               return result;
           }

           /// <summary>
           /// Redirects to a specified return URL if it belongs to the MVC website or to the site's home page.
           /// </summary>
           private ActionResult RedirectToLocal(string returnUrl)
           {
               if (Url.IsLocalUrl(returnUrl))
               {
                   return Redirect(returnUrl);
               }

               return RedirectToAction("Index", "Home");
           }

           /// <summary>
           /// Custom type that processes the result of an action method.
           /// </summary>
           internal class ChallengeResult : HttpUnauthorizedResult
           {
               public ChallengeResult(string provider, string redirectUri)
               {
                   LoginProvider = provider;
                   RedirectUri = redirectUri;
               }

               public string LoginProvider { get; set; }
               public string RedirectUri { get; set; }

               // Called to run the action result using the specified controller context
               public override void ExecuteResult(ControllerContext context)
               {
                   var properties = new AuthenticationProperties { RedirectUri = RedirectUri };

                   // Adds information into the response environment that causes the specified authentication middleware to challenge the caller
                   context.HttpContext.GetOwinContext().Authentication.Challenge(properties, LoginProvider);
               }
           }


   ```
6. Design the user interface required for external authentication on your website

   - Add buttons onto your website's sign-in page that target the _RequestSignIn_ action. Supply parameters that specify the provider type for each authentication service and optionally a return URL.

     ```xml title="Example of external authentication buttons (Razor view)"

     @using Microsoft.Owin.Security

     @{
         @* Gets a collection of the authentication services registered in your application's startup class *@
         var authServices = Context.GetOwinContext().Authentication.GetExternalAuthenticationTypes();

         @* Generates a form with buttons targeting the RequestSignIn action. Optionally pass a redirect URL as a parameter. *@
         using (Html.BeginForm("RequestSignIn", "ExternalAuthentication"))
         {
             @Html.AntiForgeryToken()
             <p>
                 @foreach (AuthenticationDescription p in authServices)
                 {
                     <button type="submit" class="btn btn-default" id="@p.AuthenticationType" name="provider" value="@p.AuthenticationType" title="Sign in using your @p.Caption account">@p.AuthenticationType</button>
                 }
             </p>
         }
     }


     ```
   - Create a view for displaying information in situations where the external authentication fails _(ExternalAuthenticationFailure_ in the example).
   - Create a view for displaying information in situations where the external authentication succeeds, but the matching Xperience user account is disabled _(Lockout_ in the example).
   - Create a view for the _SignInCallback_ action.

     > **Info:** In the most basic version of the _SignInCallback_ view, you only need to display error messages in situations where the external authentication succeeds, but Xperience is unable to create a matching user account.
     >
     > You can implement an extended version with an editing form that allows visitors to adjust and resubmit the data of the new user account (using a strongly typed view that receives the external login data from the action).

Visitors can now sign in on the website through the external authentication services that you registered in the _Startup_ class. Upon successful authentication, the system creates a corresponding user account in the shared Xperience database, along with a mapping between the user and the given authentication provider.

<!-- dev-model:mvc end -->

<!-- dev-model:core start -->

**ASP.NET Core development model.** Applies only when building with ASP.NET Core. If this page also covers MVC 5, that version is in its own block.

After you [register the required authentication services](#registering-external-authentication-services), you need to implement actions that redirect visitors to the external authentication service, process the response, and handle the corresponding user accounts in Xperience.

The following is a basic example of external authentication handling (this example uses the MVC approach):

1. Create a new controller class in your project or edit an existing one.
2. Provide instance of the following classes using dependency injection:

   ```csharp

   using System;
   using System.Threading.Tasks;
   using System.Security.Claims;

   using CMS.Helpers;
   using CMS.Core;
   using CMS.Base;

   using Microsoft.AspNetCore.Identity;
   using Microsoft.AspNetCore.Mvc;
   using Microsoft.AspNetCore.Authentication;
   using Microsoft.AspNetCore.Authorization;
   using SignInResult = Microsoft.AspNetCore.Identity.SignInResult;

   using Kentico.Membership;


   ```

   ```csharp

           private readonly SignInManager<ApplicationUser> signInManager;
           private readonly ApplicationUserManager<ApplicationUser> userManager;
           private readonly IEventLogService eventLogService;
           private readonly ISiteService siteService;

           public ExternalAuthenticationController(SignInManager<ApplicationUser> signInManager,
                                                   ApplicationUserManager<ApplicationUser> userManager,
                                                   IEventLogService eventLogService,
                                                   ISiteService siteService)
           {
               this.signInManager = signInManager;
               this.userManager = userManager;
               this.eventLogService = eventLogService;
               this.siteService = siteService;
           }


   ```
3. Add a POST action that challenges external authentication providers. Use a parameter to specify the name of the requested authentication middleware instance.
4. Add an action that handles the responses from external services.

   - Call the **SignInManager.GetExternalLoginInfoAsync** method to get login information from the external identity provided by the service.
   - Use the **SignInManager.ExternalLoginSignInAsync**method to sign in to Xperience based on external data.
5. Add logic that creates new users in the Xperience database if the first sign-in using the external data fails:

   1. Prepare a new **Kentico.Membership.ApplicationUser** object based on the external login data. You must enable the user object's **IsExternal** property.
   2. Call the **ApplicationUserManager.CreateAsync** method to create the user in the Xperience database.
   3. Call the **ApplicationUserManager.AddLoginAsync** method to create a mapping between the new user and the given authentication provider.

   ```csharp title="External authentication example"

           /// <summary>
           /// Redirects authentication requests to an external service. 
           /// Posted parameters include the name of the requested authentication middleware instance and a return URL.
           /// </summary>
           [HttpPost]
           [AllowAnonymous]
           [ValidateAntiForgeryToken]
           public IActionResult RequestExternalSignIn(string provider, string returnUrl)
           {
               // Sets a return URL targeting an action that handles the response
               string redirectUrl = Url.Action(nameof(ExternalSignInCallback), new { ReturnUrl = returnUrl });

               // Configures the redirect URL and user identifier for the specified external authentication provider
               AuthenticationProperties authenticationProperties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);

               // Challenges the specified authentiction provider, redirects to the specified 'redirectURL' when done
               return Challenge(authenticationProperties, provider);
           }

           /// <summary>
           /// Handles responses from external authentication services.
           /// </summary>
           [HttpGet]
           [AllowAnonymous]
           public async Task<IActionResult> ExternalSignInCallback(string returnUrl, string remoteError = null)
           {
               // If an error occurred on the side of the external provider, displays a view with the forwarded error message
               if (remoteError != null)
               {
                   return RedirectToAction(nameof(ExternalAuthenticationFailure));
               }

               // Extracts login info out of the external identity provided by the service
               ExternalLoginInfo loginInfo = await signInManager.GetExternalLoginInfoAsync();

               // If the external authentication fails, displays a view with appropriate information
               if (loginInfo == null)
               {
                   return RedirectToAction(nameof(ExternalAuthenticationFailure));
               }

               // Attempts to sign in the user using the external login info
               SignInResult result = await signInManager.ExternalLoginSignInAsync(loginInfo.LoginProvider, loginInfo.ProviderKey, isPersistent: false);

               // Success occurs if the user already exists in the connected database and has signed in using the given external service
               if (result.Succeeded)
               {
                   eventLogService.LogInformation("External authentication", "EXTERNALAUTH", $"User logged in with {loginInfo.LoginProvider} provider.");
                   return RedirectToLocal(returnUrl);
               }

               // The 'NotAllowed' status occurs if the user exists in the system, but is not enabled
               if (result.IsNotAllowed)
               {
                   // Returns a view informing the user about the locked account
                   return RedirectToAction(nameof(Lockout));
               }
               else
               {
                   // Attempts to create a new user in Xperience if the authentication failed
                   IdentityResult userCreation = await CreateExternalUser(loginInfo);

                   // Attempts to sign in again with the new user created based on the external authentication data
                   result = await signInManager.ExternalLoginSignInAsync(loginInfo.LoginProvider, loginInfo.ProviderKey, isPersistent: false);

                   // Verifies that the user was created successfully and was able to sign in
                   if (userCreation.Succeeded && result == SignInResult.Success)
                   {
                       // Redirects to the original return URL
                       return RedirectToLocal(returnUrl);
                   }

                   // If the user creation was not successful, displays corresponding error messages
                   foreach (IdentityError error in userCreation.Errors)
                   {
                       ModelState.AddModelError(String.Empty, error.Description);
                   }

                   return View();

                   // Optional extension:
                   // Send the loginInfo data as a view model and create a form that allows adjustments of the user data.
                   // Allows visitors to resolve errors such as conflicts with existing usernames in Xperience.
                   // Then post the data to another action and attempt to create the user account again.
                   // The action can access the original loginInfo using the AuthenticationManager.GetExternalLoginInfoAsync() method.
               }           
           }

           [HttpGet]
           [AllowAnonymous]
           public IActionResult Lockout()
           {
               return View();
           }

           [HttpGet]
           [AllowAnonymous]
           public IActionResult ExternalAuthenticationFailure()
           {
               return View();
           }

           /// <summary>
           /// Creates users in Xperience based on external login data.
           /// </summary>
           private async Task<IdentityResult> CreateExternalUser(ExternalLoginInfo loginInfo)
           {
               // Prepares a new user entity based on the external login data
               ApplicationUser user = new ApplicationUser
               {
                   UserName = ValidationHelper.GetSafeUserName(loginInfo.Principal.FindFirstValue(ClaimTypes.Name) ?? 
                                                               loginInfo.Principal.FindFirstValue(ClaimTypes.Email),
                                                               siteService.CurrentSite.SiteName),
                   Email = loginInfo.Principal.FindFirstValue(ClaimTypes.Email),
                   Enabled = true, // The user is enabled by default
                   IsExternal = true // IsExternal must always be true for users created via external authentication
                   // Set any other required user properties using the data available in loginInfo
               };

               // Attempts to create the user in the Xperience database
               IdentityResult result = await userManager.CreateAsync(user);
               if (result.Succeeded)
               {
                   // If the user was created successfully, creates a mapping between the user and the given authentication provider
                   result = await userManager.AddLoginAsync(user, loginInfo);
               }

               return result;
           }

           /// <summary>
           /// Redirects to a specified return URL if it belongs to the MVC website or to the site's home page.
           /// </summary>
           private IActionResult RedirectToLocal(string returnUrl)
           {
               if (Url.IsLocalUrl(returnUrl))
               {
                   return Redirect(returnUrl);
               }

               return RedirectToAction(nameof(Index), "Home");
           }


   ```
6. Design the user interface required for external authentication on your website

   - Add buttons onto your website's sign-in page that target the _RequestSignIn_ action. Supply parameters that specify the provider type for each authentication service and optionally a return URL.

     ```xml title="Example of external authentication buttons (Razor view)"

             @using Microsoft.AspNetCore.Authentication
             @using Microsoft.AspNetCore.Identity
             @using Kentico.Membership

             @inject SignInManager<ApplicationUser> SignInManager

             @{
                 @* Gets a collection of the authentication services registered in your application's startup class *@
                 var signInProviders = (await SignInManager.GetExternalAuthenticationSchemesAsync()).ToList();
                 if (signInProviders.Count == 0)
                 {
                     <div>
                         <p>
                             There are no external authentication services configured. See <a href="https://go.microsoft.com/fwlink/?LinkID=532715">this article</a>
                             for details on setting up this ASP.NET application to support logging in via external services.
                         </p>
                     </div>
                 }
                 else
                 {
                     @* Generates a form with buttons targeting the RequestSignIn action. Optionally pass a redirect URL as a parameter. *@
                     <form asp-controller="ExternalAuthentication" asp-action="RequestExternalSignIn" asp-route-returnurl="http://localhost/LearningKitCore/Account/SignIn" method="post" class="form-horizontal">
                         <div>
                             <p>
                                 @foreach (AuthenticationScheme provider in signInProviders)
                                 {
                                     <button type="submit" class="btn btn-default" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.Name</button>
                                 }
                             </p>
                         </div>
                     </form>
                 }
             }


     ```
   - Create a view for displaying information in situations where the external authentication fails _(ExternalAuthenticationFailure_ in the example).
   - Create a view for displaying information in situations where the external authentication succeeds, but the matching Xperience user account is disabled _(Lockout_ in the example).
   - Create a view for the _ExternalSignInCallback_ action.

     > **Info:** In the most basic version of the _ExternalSignInCallback_ view, you only need to display error messages in situations where the external authentication succeeds, but Xperience is unable to create a matching user account.
     >
     > You can implement an extended version with an editing form that allows visitors to adjust and resubmit the data of the new user account (using a strongly typed view that receives the external login data from the action).

Visitors can now sign in on the website through the external authentication services that you registered in the _Startup_ class. Upon successful authentication, the system creates a corresponding user account in the shared Xperience database, along with a mapping between the user and the given authentication provider.

<!-- dev-model:core end -->
