Microsoft External Authentication in ASP.NET Core MVC

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.

Microsoft External Authentication Login Flow:

Here, we will set up the UI (i.e., the User Interface) and configure it to redirect the request to Microsoft when clicking the Sign-In with Microsoft button. We want our login page to look like the following. Earlier, we integrated Google, and now we want to integrate Microsoft. So, the login page should have both Google and Microsoft buttons.

Microsoft External Authentication Login Flow

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:

Integrating Microsoft External Authentication in ASP.NET Core MVC

Once you provide the credentials, it will ask you to accept the permissions, as shown in the image below. Here, you need to click the Accept button, as shown in the image below.

Integrating Microsoft External Authentication in ASP.NET Core MVC

Once you click the Accept 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.

Integrating Microsoft External Authentication in ASP.NET Core MVC

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.

How to Integrate Microsoft External Authentication in ASP.NET Core MVC

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. As discussed in our previous article, this process requires setting up the Microsoft OAuth credentials and configuring your ASP.NET Core MVC application to use these credentials. Here is a step-by-step guide:

Create Microsoft OAuth Credentials:

As discussed in our previous article, we have registered our application into Microsoft and created the Client ID and Client Secret.

Install Necessary NuGet Packages:

Ensure that your ASP.NET Core MVC project has the necessary NuGet packages installed. You would typically install Microsoft.AspNetCore.Authentication.MicrosoftAccount package. So please install the Microsoft.AspNetCore.Authentication.MicrosoftAccount package from NuGet Package Manager Console.

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.

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 is part of the authentication middleware and configures the Microsoft Account as an external login provider.

  • Purpose: The AddMicrosoftAccount method integrates Microsoft Account authentication into your ASP.NET Core application. It allows users to sign in using their Microsoft credentials (like Outlook, Office 365, or Xbox accounts).
  • Dependencies: It often requires the installation of a NuGet package, typically Microsoft.AspNetCore.Authentication.MicrosoftAccount.
  • Configuration: The method requires configuration settings, specifically the ClientId and ClientSecret, which you obtain by registering your application in the Microsoft Azure portal.
  • Functionality: When AddMicrosoftAccount is called under the hood, 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 your application.
Login View:

Since we have integrated both Google and Microsoft authentication, we need to display the respective external login provider button automatically on the Login view. We have written the logic in a generic manner, 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?.Count == 0)
            {
                <div>No external logins configured</div>
            }
            else
            {
                <form method="post" asp-action="ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl">
                    <div>
                        @foreach (var provider in Model.ExternalLogins)
                        {
                            <button onclick="externalLogin(@provider.Name, @Model.ReturnUrl)" type="submit" class="btn btn-primary"
                                    name="provider" value="@provider.Name"
                                    title="Log in using your @provider.DisplayName account">
                                @provider.DisplayName
                            </button>
                        }
                    </div>
                </form>
            }
        }
    </div>
</div>
Redirect Request to Microsoft

When the Microsoft button clicks, our ASP.NET Core MVC application must redirect the request to Microsoft for authentication. This is done by ExternalLogin() action in 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 change the ExternalLogin action method. The following is the ExternalLogin action method of the Account Controller.

[AllowAnonymous]
[HttpPost]
public IActionResult ExternalLogin(string provider, string returnUrl)
{
    //This call will generate a URL that directs to the ExternalLoginCallback action method in the Account controller
    //with a route parameter of ReturnUrl set to the value of returnUrl.
    var redirectUrl = Url.Action(action: "ExternalLoginCallback", controller: "Account", values: new { ReturnUrl = returnUrl });

    // Configure the redirect URL, provider and other properties
    var properties = signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);

    //This will redirect the user to the external provider's login page
    return new ChallengeResult(provider, properties);
}
Handle External Login Information Received from Microsoft

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’s and Microsoft’s external authentication. So, again, we don’t need to make any changes to the ExternalLoginCallback action method. The following is the ExternalLoginCallback action method of the Account Controller.

[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string? returnUrl, string? remoteError)
{
    returnUrl = returnUrl ?? Url.Content("~/");

    LoginViewModel loginViewModel = new LoginViewModel
    {
        ReturnUrl = returnUrl,
        ExternalLogins = (await signInManager.GetExternalAuthenticationSchemesAsync()).ToList()
    };

    if (remoteError != null)
    {
        ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}");

        return View("Login", loginViewModel);
    }

    // Get the login information about the user from the external login provider
    var info = await signInManager.GetExternalLoginInfoAsync();
    if (info == null)
    {
        ModelState.AddModelError(string.Empty, "Error loading external login information.");

        return View("Login", loginViewModel);
    }

    // If the user already has a login (i.e., if there is a record in AspNetUserLogins table)
    // then sign-in the user with this external login provider
    var signInResult = await signInManager.ExternalLoginSignInAsync(info.LoginProvider,
        info.ProviderKey, isPersistent: false, bypassTwoFactor: true);

    if (signInResult.Succeeded)
    {
        return LocalRedirect(returnUrl);
    }

    // If there is no record in AspNetUserLogins table, the user may not have a local account
    else
    {
        // Get the email claim value
        var email = info.Principal.FindFirstValue(ClaimTypes.Email);

        if (email != null)
        {
            // Create a new user without password if we do not have a user already
            var user = await userManager.FindByEmailAsync(email);

            if (user == null)
            {
                user = new ApplicationUser
                {
                    UserName = info.Principal.FindFirstValue(ClaimTypes.Email),
                    Email = info.Principal.FindFirstValue(ClaimTypes.Email),
                    FirstName = info.Principal.FindFirstValue(ClaimTypes.GivenName),
                    LastName = info.Principal.FindFirstValue(ClaimTypes.Surname),
                };

                //This will create a new user into the AspNetUsers table without password
                await userManager.CreateAsync(user);
            }

            // Add a login (i.e., insert a row for the user in AspNetUserLogins table)
            await userManager.AddLoginAsync(user, info);

            //Then Signin the User
            await signInManager.SignInAsync(user, isPersistent: false);

            return LocalRedirect(returnUrl);
        }

        // If we cannot find the user email we cannot continue
        ViewBag.ErrorTitle = $"Email claim not received from: {info.LoginProvider}";
        ViewBag.ErrorMessage = "Please contact support on info@dotnettutorials.net";

        return View("Error");
    }
}

Now, run the application and check the functionality; it should work as expected.

In the next article, I will discuss How to Create Facebook OAuth Credentials. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *