Back to: ASP.NET Core Identity Tutorials
Integrating Microsoft External Authentication in ASP.NET Core MVC
In this article, I will discuss Integrating Microsoft External Authentication in ASP.NET Core MVC Application. Please read our previous article discussing Microsoft Account External Login Setup. This is a continuation of our previous article, in which we discussed registering our application with Microsoft for external authentication and generating the Client ID and Client Secret.
Integrating Microsoft External Authentication in ASP.NET Core MVC
Integrating Microsoft 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 Microsoft sign-in page when the user clicks the Sign-In with Microsoft button.
We want our login page to look like the following. Earlier, we integrated Google, and Now we will see how to integrate Microsoft external authentication. So, the login page should have both Google and Microsoft buttons.
When the user clicks on the Microsoft button, it will open the following page asking the user to login using his Microsoft credentials as follows:
Once you provide the credentials, it will ask you to accept the permissions, as shown in the below image. Here, you need to click the Accept button, as shown in the image below.
Once you click the Accept button, the user will be logged in to our application. On successful login, it will make an entry into 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 image below. The ProviderKey will be unique for each user.
Integrating Microsoft Authentication in ASP.NET Core MVC
Integrating Microsoft Authentication in an ASP.NET Core MVC application using ASP.NET Core Identity involves several steps. The process requires setting up the Microsoft OAuth credentials, which we have already discussed, and configuring our ASP.NET Core application to use these credentials. Let us proceed and understand this step by step:
Create Microsoft OAuth Credentials:
We have already registered our application with Microsoft and created the Client ID and Client Secret, which are required when integrating Microsoft Authentication into our ASP.NET Core application.
Install Necessary NuGet Packages:
First, we need to install Microsoft.AspNetCore.Authentication.MicrosoftAccount package, which is required for Microsoft External authentication. So please install the package from NuGet by executing the following command in the Package Manager Console.
Install-Package Microsoft.AspNetCore.Authentication.MicrosoftAccount
Configure Authentication Services:
In the Program.cs file, configure the authentication services to include Microsoft authentication. You need to use the Client ID and Client Secret obtained from Microsoft. We have already configured the Google OAuth; let us add the Microsoft authentication. So, please add the following code to the Program.cs file.
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]"; });
What is the AddMicrosoftAccount Method in ASP.NET Core
In ASP.NET Core, the AddMicrosoftAccount method integrates Microsoft Account authentication into your web application. Under the hood, when AddMicrosoftAccount is called, it configures the OAuth 2.0 authentication flow with Microsoft’s identity platform. This flow includes redirecting users to a Microsoft login page, handling the callback with an authentication token, and creating a user session in our application.
Login View:
Since we have both Google and Microsoft authentication integrated, we need to display the respective external login provider button in the 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 Microsoft button is clicked, our ASP.NET Core MVC application must redirect the request to Microsoft for authentication. This is done by the ExternalLogin() action method of our AccountController. The code in this method is written in a generic way, so it works for both Google and Microsoft authentication. So, we don’t need to make any changes to the ExternalLogin action method. The following is our Account Controller’s 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
After Microsoft successfully authenticates the user, the request is redirected back to our application, and the following ExternalLoginCallback action method of the Account Controller is executed. The code in this method is also written in a generic way, so it works for both Google and Microsoft external authentication. So, again, we don’t need to make any changes to the ExternalLoginCallback action method. The following is the Account Controller’s 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.
How Does Microsoft OAuth External Authentication Work?
Microsoft external authentication, like Google authentication, also follows the OAuth 2.0 protocol to allow third-party applications to access Microsoft services securely without needing to handle or store user credentials. This is often used to access services like Microsoft Graph for Outlook, OneDrive, or Azure management APIs. For a better understanding of the workflow of Microsoft OAuth 2.0, please look at the below image:
Here’s a step-by-step explanation of how Microsoft OAuth credentials work based on the diagram:
Step1: User Requests Token (Your App):
When the user clicks to sign in using Microsoft (e.g., using their Microsoft account or organizational account), your application will request an OAuth token by redirecting the user to the Microsoft login page. The request includes parameters such as the client ID, scope (which details the permissions your app requires), response type (code for authorization code grant), and a redirect URI. The scope could be defined to access various Microsoft services like user emails, calendars, or files on OneDrive.
Step2: User Login and Consent (Microsoft Identity Platform):
The user is presented with a Microsoft login screen where they can either sign in to their personal Microsoft account or an account managed by an organization (Azure AD). If the user is already logged in, they will be automatically signed in. After login, Microsoft displays a consent screen that details the permissions requested by your application. If the user approves, Microsoft will grant an authorization code. This process ensures the user understands what data the application will access and the actions it can perform.
Step3: Authorization Code (Microsoft Identity Platform to Your App):
After the user successfully logs in and consents, Microsoft’s servers will redirect the user back to your application, providing an authorization code. That means Microsoft sends the authorization code to the redirect URI specified in the initial request. This authorization code is temporary and serves as an intermediary step in requesting an access token. The application captures this code from the query parameters of the redirected URL.
Step4: Exchange Authorization Code for Access Tokens (Your App):
Your app then sends the authorization code, along with its client credentials (client ID and client secret), to Microsoft’s token endpoint. This request is made to Microsoft Identity Platform servers. In response, Microsoft returns an access token (and optionally a refresh token). The access token can be used to access user data from Microsoft APIs, such as Microsoft Graph.
Step5: Token Response (Microsoft Identity Platform to Your App):
If the request is valid, Microsoft responds with an access token, which is used for authentication and authorization and typically a refresh token. The access token has a limited lifespan, typically lasting a few hours, while the refresh token can last much longer. These tokens are then used by the application to authenticate requests made to Microsoft services on behalf of the user.
Step6: Use Access Token to Call Microsoft API (Your App):
With the access token, your app can make requests to Microsoft APIs (e.g., Microsoft Graph API) to access resources like user profile data, emails, calendars, files, etc. The token is included in the HTTP authorization header of requests. When the access token expires, the application can use the refresh token (if available) to obtain a new access token without requiring the user to log in again.
The OAuth 2.0 process with Microsoft protects user credentials and also allows users to control which data the application can access, similar to how Google handles external authentication. This protocol is widely used because of its flexibility and security, providing effective delegation capabilities in a way that minimizes the risks of token theft or unauthorized access.
In the next article, I will discuss How to Create Facebook OAuth Credentials. In this article, I explain how to integrate Microsoft authentication in ASP.NET Core MVC. I hope you enjoy this article, Integrating Microsoft Authentication in ASP.NET Core MVC.