Getting Started with Pre-Requisites
First of all create an OAuth App by following the steps explained in this video - https://youtu.be/55z-SOpiBTE.
Secondly, take note of the nuget package that you will have to add to your ASPNET Core project - Microsoft.AspNetCore.Authentication.Google.
Install-Package Microsoft.AspNetCore.Authentication.Google
This package contains most of the boilerplate code for completing the login process.
Video Explanation (see it happen!)
Please watch the following youtube video:
Login and Profile Pages
I have already created an ASPNET Core project that you can obtain from the downloads attached to this video. So we'll now examine the files and the code written there.
Open the solution explorer and locate the Pages folder. We have two files - the first is the Index.cshtml home page. This file has a link to the second page called Profile. So when the user runs the project he will see this link.
// Index.cshtml file in the Pages folder // home page that shows a link @page <h1>Welcome Home!</h1> <p> When you click the link below you will be taken to your google account for authentication. </p> <a href="/Profile">Click to Login by Google</a>
Let's have a look at the Profile page. Double-click to open this page.
This page has been marked with the [Authorize] attribute. This means that this is a protected page. When the user tries to open this page he will automatically receive a challenge that will redirect him to the google login page. We'll discuss that code very soon.
// Profile.cshtml file @page @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers @using System.Security.Claims @using Microsoft.AspNetCore.Authentication @using Microsoft.AspNetCore.Authentication.Cookies <h1>You are Logged in!</h1> <img src="@User?.FindFirstValue(" urn:google:picture")" /> <h3>Name: @User?.Identity?.Name </h3> <h3>EMail: @User?.FindFirstValue(ClaimTypes.Email) </h3> <h3><a asp-page-handler="SignOut">Sign out</a></h3> @functions { public async Task<IActionResult> OnGetSignOut() { // kills the login cookie await HttpContext.SignOutAsync( CookieAuthenticationDefaults.AuthenticationScheme); // redirect to web home or login page return LocalRedirect("/"); } }
This page merely displays the google profile picture, then the name of the user as obtained from his google account, and also his email.
There is a signout link also. The code for signout kills the login cookie.
Program.cs file
Lastly, let's now examine the program.cs file. Open the solution explorer and locate the program.cs file. Double click to open it.
You will have to install this nuget package for the code to compile. Then we have the namespaces, and the usual calls to CreateBuilder
and AddRazorPages
// Install-Package Microsoft.AspNetCore.Authentication.Google using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.Google; using System.Security.Claims; var builder = WebApplication.CreateBuilder(); builder.Services.AddRazorPages(); builder .Services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; options.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme; }) .AddCookie() .AddGoogle(googleOptions => { // refer: https://youtu.be/55z-SOpiBTE googleOptions.ClientId = "441---.com"; googleOptions.ClientSecret = "GO---Xi"; // get profile pic also googleOptions .ClaimActions .MapJsonKey("urn:google:picture", "picture", "url"); googleOptions.AccessDeniedPath = "/"; googleOptions.Events.OnTicketReceived += (ticket) => { // get the email String email = ticket.Principal.FindFirstValue(ClaimTypes.Email); // or ticket.Principal.FindFirstValue(ClaimTypes.NameIdentifier) // for a unique id maintained by the social media platform // make a database check if email is registered // suppose we get true after a database query bool isEmailRegistered = true; if (!isEmailRegistered) { // add to database, if required isEmailRegistered = true; } // make a database check if email is permitted // by your server String? role = null; // make a database check and // query a role to be applied if (isEmailRegistered) { // suppose we get Admin after database check role = "Admin"; ticket.Principal?.AddIdentity( new ClaimsIdentity(new[] { new Claim(ClaimTypes.Role, role) }, GoogleDefaults.AuthenticationScheme )); } // login success! now we can create a persistent // cookie to remember the user for a certain duration if (!String.IsNullOrEmpty(role)) { // remember for 5 minutes or any custom duration if (null != ticket.Properties) { ticket.Properties.ExpiresUtc = DateTimeOffset.UtcNow.AddMinutes(5); ticket.Properties.IsPersistent = true; } } else { // access denied if role could not be attached ticket.Response.StatusCode = StatusCodes.Status401Unauthorized; // redirect to access denied ticket.ReturnUri = "/AccessDenied"; } return Task.CompletedTask; }; }); builder.Services.AddAuthorization(); var app = builder.Build(); app.UseAuthentication(); app.UseAuthorization(); app.MapRazorPages(); app.Run();
Authentication service is configured next, with the DefaultScheme
and DefaultChallengeScheme
set to "Cookies" and "Google" exactly as shown.
The call to AddCookie
configures various cookie defaults.
The most important settings are done through the AddGoogle
extension. The values for ClientId and ClientSecret must match exactly to the ones as shown on your google console page.
MapJsonKey
can be used to obtain a url for the profile picture. If you do not add this call, then you will not receive the url to the profile picture.
Lastly, we have subscribed to the TicketReceived event. This event is raised after a user has successfully authenticated from the login page of google.
We can extract the email at this point. Alternatively we could have obtained NameIdentifier which is a unqiue id maintained by social media platforms.
Once you have the email id, then you can process it in whatever manner you like.
You might want to determine if the email exists in your database already. If the user doesn't exist, then you might want to add him and a default profile to your database.
Then you can assign a role to the user. We have assumed that after query, a role called Admin is assigned to the user. Next, this role is added to the ClaimsPrincipal.
Once a role is assigned, the user has been authenticated. We can optionally create a persistent cookie so that the user doesn't have to login very frequently.
If no role could be applied, we can treat the user as not authenticated, so we can redirect him to an access denied page. We haven't added this page to this project - you can add it yourself.
After that we have the usual lines for running the app.
Run the Project
Run the project to open the home page.
See the linked video for a clearer explanation.Click on the link Click to Login by Google. This link points to the protected profile page. But since we need an authorization, we are redirected to the google login website.
Click on the test email and make a login. Allow the process to complete.
We observe that we reach the protected profile page. User email, name and profile picture, everything appears as expected.
Now click on the sign out link. We observe that we reach the home page again.
Thus we have a working example of google based authentication and authorization. You can similarly implement facebook, twitter and other authentications. Thanks!
This Blog Post/Article "(C# ASP.NET Core) Social Media based Authentication, Remember Me and Signout" by Parveen is licensed under a Creative Commons Attribution-NonCommercial-ShareAlike 4.0 International License.