Authenticate users using Microsoft account in ASP.NET Core

At times you want to integrate external logins with your ASP.NET Core
applications. External logins include Microsoft accounts, Google, Facebook, or
Twitter. When you use external logins, the login details such as user name and
password aren't stored in your local database. They are stored in some external
store and you are required to "get in touch" with that external source to
authenticate your website users. In this article you will learn to integrate
Microsoft Account based login mechanism in your ASP.NET Core web app.
Before you create your ASP.NET Core project you need to create an App
Registration in the Azure Portal. So, go to Azure Portal and search for App
Registrations.

Click on New registration button and create a new App registration entry
called My Te

Make sure to select the third radio button that allows you to sign in using
Azure AD + Personal Microsoft Accounts. Also, enter the Redirect URI textbox
with your ASP.NET Core web app's base URL appended with "/signin-microsoft". We
haven't created a web app yet but since we know the default port number used by
the development web server we can enter it in the above textbox. If you plan to
use IIS Express as the development web server then you can first create a web
app to know the port number OR modify the Redirect URI later on.
Complete the app registration by clicking the Register button at the bottom.
Next, go to the new created Azure app registration's overview page and note
down its client ID.

Now go to Certificates and Secrets section and create a new Client Secret.

Note down this client secret carefully.
This completes the Azure portal related tasks.
Now create a new ASP.NET Core MVC application. You can either use empty
project template or MVC project template but do not select any particular
authentication scheme. We will do all the configuration ourselves in order to
understand how it works.

Then add the NuGet package -
Microsoft.AspNetCore.Authentication.MicrosoftAccount.

Now open appsettings.json file and store the Client ID and Client Secret
noted earlier as shown below:
"Authentication": {
"ClientId": "your_client_id",
"ClientSecret": "your_client_secret"
}
Next, open the Startup class and add this code in the ConfigureServices()
method:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllersWithViews();
services.AddRazorPages();
services.AddAuthentication(options =>
{
options.DefaultScheme =
CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme =
MicrosoftAccountDefaults.AuthenticationScheme;
}).AddCookie()
.AddMicrosoftAccount(o =>
{
o.ClientId = Configuration
["Authentication:ClientId"];
o.ClientSecret = Configuration
["Authentication:ClientSecret"];
});
}
The above code uses AddAuthentication() method to set default authentication
scheme to cookies and default challenge scheme to Microsoft account.
The code also calls AddCookie() method to enable authentication cookies. The
AddMicrosoftAccount() method configures the properties for Microsoft Account.
Here we set the ClientId and ClientSecret properties to their respective values
from the configuration file.
Now add the following calls in the Configure() method:
...
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
...
...
We wire authentication middleware and authorization middleware using
UseAuthentication() and UseAuthorization() calls.
Let's quickly see the UI of the app before we actually go to the view markup
and code.
When you run the app you will see this :

When you click the Sign In button, you will be shown Microsoft account's Sign
In page.

Upon successfully signing in using your Microsoft account credentials you
will be shown this:

This page shown that you have successfully signed in to the app. There is a
link to an action protected using [Authorize] attribute. And there are two
buttons - Sign Out and Sign Out Completely. Clicking on the first button logs
you out of the current app whereas clicking on the second button logs you out of
the Microsoft account itself.
Let's see the markup and code that makes the above discussed UI work.
Open Index.cshtml and write this code:
@{
Layout = "";
}
@if (User.Identity.IsAuthenticated)
{
<h1>You are signed in</h1>
<h2><a asp-action="Privacy">
Go to protected page</a></h2>
<form asp-controller="Home"
asp-action="SignOut">
<button type="submit" name="signOutType"
value="app">Sign Out</button>
<button type="submit" name="signOutType"
value="all">Sign Out Completely</button>
</form>
}
else
{
<h1>You are not signed in</h1>
<form asp-controller="Home"
asp-action="SignIn">
<button type="submit">
Sign In</button>
</form>
}
Here, we first check whether a user is logged in or not using
User.Identity.IsAuthenticated property.
If a user is signed in we display a message and render a hyperlink pointing
to Privacy() action of HomeController. The Privacy() action is marked with
[Authorize] attribute and hence requires authenticated user to run. The Sign Out
button and Sign Out Completely button both submit to SignOut() action of
HomeController. We have set name and value attributes of these <button> element.
Inside the SignOut() action, these values will help us detect which button was
clicked.
If a user is not signed in we display a message accordingly and also show
Sign In button. The Sign In button submits to SignIn() action of HomeController.
Now that you know the UI of the app, let's discuss the SignIn(), SiignOut(),
and Privacy() actions.
The SignIn() action is shown below:
public IActionResult SignIn()
{
var props = new AuthenticationProperties();
props.RedirectUri = "/Home/SignInSuccess";
return Challenge(props);
}
Inside the SignIn() action we create a new AuthenticationProperties object.
And we set the RedirectUri property to SignInSuccess() action of HomeController.
When a user successfully signs in using a Microsoft account, we want out app to
know that the login was successful. So, upon successful login we redirect the
control to SignInSuccess() action.
Then we call Challenge() method of the Controller base class and pass the
AuthenticationProperties object to it. The Challenge() method triggers your
app's authentication scheme. Recollect that we have set the authentication
scheme in the AddAuthentication() method earlier.
The SignInSuccess() action doesn't do much in this case, it simply redirects
to the Index() action. But you can add some app specific processing here if
required.
public IActionResult SignInSuccess()
{
return RedirectToAction("Index");
}
The SignOut() action is shown below:
public IActionResult SignOut(string signOutType)
{
if (signOutType == "app")
{
HttpContext.SignOutAsync().Wait();
}
if (signOutType == "all")
{
return Redirect("https://
login.microsoftonline.com/common/
oauth2/v2.0/logout");
}
return RedirectToAction("Index");
}
If the uses has clicked on the Sign Out button we call SignOutAsync() method
of HttpContext. The SignOutAsync() method simple deletes the authentication
cookie, it doesn't log the use out of the Microsoft Account as such.
If the user has clicked on the Sign Out Completely button we redirect the
request to the specified URL. This URL triggers the Microsoft Account's log out
process and you will be asked this:

And when you pick an account, you are logged out of it:

The Privacy() action is decorated with [Authorize] attribute like this:
[Authorize]
public IActionResult Privacy()
{
return View();
}
Just for the sake of confirming the user's name you can write this in the
Privacy.cshtml file:
@{
Layout = "";
}
<h1>Welcome @User.Identity.Name</h1>
Run the application and check the complete sign-in and sign-out flow.
You can read more about external login providers in the official
documentation
here.
That's it for now! Keep coding!!