Back to: ASP.NET Core Identity Tutorials
Integrating Facebook External Authentication in ASP.NET Core MVC
In this article, I will discuss Integrating Facebook External Authentication in ASP.NET Core MVC Application. Please read our previous article discussing Facebook Account External Login Setup. This is a continuation of our previous article, in which we discussed registering our application with Facebook for external authentication and generating the Client ID and Client Secret.
Integrating Facebook External Authentication in ASP.NET Core MVC
Integrating the Facebook Account for external authentication in an ASP.NET Core MVC application involves several steps. Now, we will set up the UI (i.e., the User Interface) and configure it to redirect the request to the Facebook sign-in page when the user clicks the Sign-In with Facebook button.
Facebook External Authentication Login Flow:
We want our login page to look like the following. We integrated Google and Microsoft earlier, and now we will integrate Facebook. So, the login page should have Google, Microsoft, and Facebook buttons, as shown in the below image.
When the user clicks on the Facebook button, it will open the following page, asking the user to log in using his Facebook credentials, as shown in the image below.
Once you enter the Facebook ID and Password, the following page will open. Please click on the Continue button as shown in the below image.
Once you click the Continue button, the user will be logged in to your application. On successful login, it will store the user details in the AspNetUsers table without a password, as shown in the image below.
It will also store the provider details in the AspNetUserLogins table, as shown in the below image. The ProviderKey will be unique for each user.
Integrating Facebook External Authentication in ASP.NET Core MVC
Integrating Facebook External Authentication in an ASP.NET Core MVC application using ASP.NET Core Identity involves several steps. This process requires setting up the Facebook OAuth credentials, which we have already discussed, and configuring our ASP.NET Core MVC application to use these credentials.
Install Necessary NuGet Packages:
First, we need to install Microsoft.AspNetCore.Authentication.Facebook package, which is required for Facebook External authentication. So please install the package from NuGet by executing the following command in the Package Manager Console.
Install-Package Microsoft.AspNetCore.Authentication.Facebook
Configure Authentication Services:
In the Program.cs file, configure the authentication services to include Facebook authentication. You need to use the Client ID and Client Secret obtained from Facebook. We have already configured the Google and Microsoft, let us add the Facebook OAuth authentication.
builder.Services.AddAuthentication() .AddGoogle(options => { options.ClientId = "[Your Google Client ID]"; options.ClientSecret = "[Your Google Client Secret]"; // You can set other options as needed. }) .AddMicrosoftAccount(microsoftOptions => { microsoftOptions.ClientId = "[Your Microsoft Client ID]"; microsoftOptions.ClientSecret = "[Your Microsoft Client Secret]"; }) .AddFacebook(facebookOptions => { facebookOptions.ClientId = "[Your Facebook Client ID]"; facebookOptions.ClientSecret = "[Your Facebook Client Secret]"; });
What is the AddFacebook Method in ASP.NET Core?
The AddFacebook method in ASP.NET Core applications enables our ASP.NET Core application to authenticate users via their Facebook accounts. The method typically requires an App ID and App Secret, which we get from your Facebook app’s settings in the Facebook Developers portal.
Login View:
We have generically written the logic, so we aren’t required to change the login view. The page will display the respective button based on the registered external providers. The following is our Login.cshtml view.
@model LoginViewModel <div class="row"> <div class="col-md-6"> <h1>Local Account Login</h1> <hr /> <form method="post" asp-action="Login" asp-controller="Account"> <input type="hidden" name="ReturnUrl" value="@Model.ReturnUrl" /> <div asp-validation-summary="All" class="text-danger"></div> <div class="form-group"> <label asp-for="Email"></label> <input asp-for="Email" class="form-control" /> <span asp-validation-for="Email" class="text-danger"></span> </div> <div class="form-group"> <label asp-for="Password"></label> <input asp-for="Password" class="form-control" /> <span asp-validation-for="Password" class="text-danger"></span> </div> <div class="form-group"> <div class="checkbox"> <label asp-for="RememberMe"> <input asp-for="RememberMe" /> @Html.DisplayNameFor(m => m.RememberMe) </label> </div> </div> <button type="submit" class="btn btn-primary">Login</button> </form> </div> <div class="col-md-6"> <h1>External Login</h1> <hr /> @if (Model.ExternalLogins == null || Model.ExternalLogins.Count == 0) { <div>No external logins configured</div> } else { <div> @foreach (var provider in Model.ExternalLogins) { <button type="button" class="btn btn-primary external-login-btn" data-provider="@provider.Name" data-url="@Url.Action("ExternalLogin", "Account", new { provider = provider.Name, returnUrl = Model.ReturnUrl })" title="Log in using your @provider.DisplayName account"> @provider.DisplayName </button> } </div> } </div> </div> @section Scripts { <script type="text/javascript"> document.querySelectorAll('.external-login-btn').forEach(button => { button.addEventListener('click', function () { var provider = this.getAttribute('data-provider'); var url = this.getAttribute('data-url'); // Define popup window size var width = 500; var height = 600; var left = (window.innerWidth / 2) - (width / 2); var top = (window.innerHeight / 2) - (height / 2); // Open the popup window var popup = window.open(url, provider, `width=${width},height=${height},top=${top},left=${left},resizable=yes`); // Monitor the popup to check if it closes var checkPopup = setInterval(function () { if (popup.closed) { clearInterval(checkPopup); // Optionally, reload or fetch updated user state location.reload(); // Refresh the parent window to reflect login state } }, 500); // Check every 500ms if the popup is closed }); }); </script> }
Redirect Request to Microsoft
When the Facebook button is clicked, our ASP.NET Core MVC application must redirect the request to Facebook for external authentication. This is done by ExternalLogin action method of Account Controller. The code in this method is written in a generic way. So, we don’t need to make any changes to the ExternalLogin action method.
[AllowAnonymous] [HttpGet] public IActionResult ExternalLogin(string provider, string returnUrl) { // Generate a URL for the "ExternalLoginCallback" action method in the "Account" controller. // This URL includes the returnUrl as a route parameter, which will be used to redirect the user // back to the original page they were trying to access after a successful external login. var redirectUrl = Url.Action( action: "ExternalLoginCallback", // The name of the callback action method. controller: "Account", // The name of the controller containing the callback method. values: new { ReturnUrl = returnUrl } // Pass the returnUrl as a parameter to the callback method. ); // Configure authentication properties for the external login. // The "ConfigureExternalAuthenticationProperties" method sets up parameters needed for the external provider, // such as the login provider name (e.g., Google, Facebook) and the redirect URL to be used after login. var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); // Redirect the user to the external provider's login page (e.g., Google or Facebook). // The "ChallengeResult" triggers the external authentication process, which redirects the user // to the external provider's login page using the configured properties. return new ChallengeResult(provider, properties); }
Handle External Login Information Received from Facebook
Once the user is successfully authenticated by Facebook, the request is redirected back to our application, and the following ExternalLoginCallback action method of Account Controller is executed. The code in this method is also written in a generic way. So, again, we don’t need to make any changes to the ExternalLoginCallback action method.
[AllowAnonymous] public async Task<IActionResult> ExternalLoginCallback(string? returnUrl, string? remoteError) { // If no returnUrl is provided, default to the application's home page. returnUrl = returnUrl ?? Url.Content("~/"); // Check if an error occurred during the external authentication process. // If so, display an alert to the user and close the popup window. if (remoteError != null) { return Content($"<script>alert('Error from external provider: {remoteError}'); window.close();</script>", "text/html"); } // Retrieve login information about the user from the external login provider (e.g., Google, Facebook). // This includes details like the provider's name and the user's identifier within that provider. var info = await signInManager.GetExternalLoginInfoAsync(); // If the login information could not be retrieved, display an error message // and close the popup window. if (info == null) { return Content($"<script>alert('Error loading external login information.'); window.close();</script>", "text/html"); } // Attempt to sign in the user using their external login details. // If a corresponding record exists in the AspNetUserLogins table, the user will be logged in. var signInResult = await signInManager.ExternalLoginSignInAsync( info.LoginProvider, // The name of the external login provider (e.g., Google, Facebook). info.ProviderKey, // The unique identifier of the user within the external provider. isPersistent: false, // Indicates whether the login session should persist across browser restarts. bypassTwoFactor: true // Bypass two-factor authentication if enabled. ); // If the external login succeeds, redirect the parent window to the returnUrl // and close the popup window. if (signInResult.Succeeded) { return Content($"<script>window.opener.location.href = '{returnUrl}'; window.close();</script>", "text/html"); } // If the user does not have a corresponding record in the AspNetUserLogins table, // attempt to create a new account using the user's email from the external provider. var email = info.Principal.FindFirstValue(ClaimTypes.Email); // Retrieve the user's email from the external login provider. if (email != null) { // Check if a local user account with the retrieved email already exists. var user = await userManager.FindByEmailAsync(email); // If no local account exists, create a new user in the AspNetUsers table. if (user == null) { user = new ApplicationUser { UserName = email, // Set the username to the user's email. Email = email, // Set the email. FirstName = info.Principal.FindFirstValue(ClaimTypes.GivenName), // Retrieve and set the user's first name. LastName = info.Principal.FindFirstValue(ClaimTypes.Surname) // Retrieve and set the user's last name. }; // Create the new user in the database. await userManager.CreateAsync(user); } // Link the external login to the newly created or existing user account. // This inserts a record into the AspNetUserLogins table. await userManager.AddLoginAsync(user, info); // Sign in the user locally after linking their external login. await signInManager.SignInAsync(user, isPersistent: false); // Redirect the parent window to the returnUrl and close the popup window. return Content($"<script>window.opener.location.href = '{returnUrl}'; window.close();</script>", "text/html"); } // If the email claim is not provided by the external login provider, // display an error message and close the popup window. return Content($"<script>alert('Email claim not received. Please contact support.'); window.close();</script>", "text/html"); }
Now, run the application and check the functionality; it should work as expected.
In the next article, I will discuss ASP.NET Core Secret Manager. In this article, I explain Integrating Microsoft Authentication in ASP.NET Core MVC. I hope you enjoy this article, Integrating Microsoft Authentication in ASP.NET Core MVC.