find and solve || findandsolve.com
Please wait.....

Google external login setup in ASP .NET Core

Google external login setup in ASP .NET Core

This article demonstrates how to build a .NET Core 5 that enables users to sign in using OAuth with credentials from external authentication providers. This tutorial shows you how to enable users to sign in with their Google account using the .NET Core 5. Users logged in using their existing credentials from third-party applications, like as Googe, Facebook, Twitter, Github, Microsfot, and so on. In this article, I am going authentication of the .NET 5 app using a Google account.

ASP.NET Core is a cross-platform, high-performance, open-source framework for building modern, cloud-based, Internet-connected applications. It includes support for hosting on Windows, Linux, and macOS, and can be used to build web applications, IoT applications, and mobile backends.

To implement Google authentication in an ASP.NET Core application, you can use Google.AspNetCore.Authentication.Google package, which provides Google OAuth2 authentication support for ASP.NET Core. Here is an example of how to configure Google authentication in an ASP.NET Core app:


To set up Google external login in ASP.NET Core, you need to perform the following steps:

  • Register your application with Google by following the instructions here: https://developers.google.com/identity/sign-in/web/sign-in#before_you_begin
  • Make a note of the client ID and client secret that Google provides for your application.
  • In your ASP.NET Core application, install the following NuGet packages:
  • Microsoft.AspNetCore.Authentication.Google
  • Microsoft.AspNetCore.Authentication.OAuth
  • In the ConfigureServices method of your application's Startup class, add the following code to configure the Google authentication options:
services.AddAuthentication().AddGoogle(options =>
{
    options.ClientId = "<your client id>";
    options.ClientSecret = "<your client secret>";
});
  • In the Configure method of your Startup class, add the following code to enable the authentication middleware:
app.UseAuthentication();
  • In your login page or login partial view, add a link or button that the user can click to initiate the login process with Google. The link or button should point to the following URL:
/Account/ExternalLogin?provider=Google
  • When the user clicks the link or button, they will be redirected to Google to sign in and grant permission to your application. If the authentication is successful, the user will be redirected back to your application and logged in.

That's it! Your application is now set up to allow users to log in with their Google account. You can customize the authentication process further by setting additional options in the GoogleAuthenticationOptions class. For more information, you can refer to the documentation here: https://docs.microsoft.com/en-us/dotnet/api/microsoft.aspnetcore.authentication.google.googleauthenticationoptions?view=aspnetcore-6.0

Download source code : https://findandsolve.com/source-code/code-how-to-add-googlle-authentication-to-dot-net-5-core

Create a Google API Console project and client ID

The first thing we need to  set up a project in Google Cloud Console.

We need to clientId and clientSecret so this help the login provider to understand which application is requesting for the authenticated user's profile to provide user security.

Setup Google Console Project

These steps will allow us to set up a project like as given below.

  1. At the top of the page, next to the Google Cloud Platform logo, there should be a project dropdown list.click on Create Project. A New Project window opens up, enter the App name and other details. as given below.

2. Enter the  project name in Project Name filed and click on the Create button.

A page will be presented that will give us information about the project, live as information and the number of requests.



Create Credentials 

  1. Click on the menu icon which is the top-left  page. Navigate to API & Services and click on Credentials.


2. Click on the Create Crendentials button and select OAuth Client ID.


After click OAuth Client ID.


After clik on Configure Consent Screen button and following the next steps.

  1. Select User Type External
  2. Click on Create button.

OAuth Crendentials

  1. Click on the menu icon in the top-left hand side of the page. Navigate to API & Services and click on Credentials.
  2. Click on the Create Crendentials button and select OAuth Client ID.
  3. Select Application Type 'Web Application'
  4. Enter the  application a Name if required,

Under the Authorized redirect URIs, enter https://localhost:1641/signin-google, which is where the redirection happens.

Click on Create button and you have a popup showing up the ClientId and ClientSecret.

Integrating Google Auth in .NET 5.0

Now I am going to integrate Google authentication in our .NET 5.0 application so first create new project like as given step by.

  1. Open visual studio 2019
  2. Create a new project.
  3. Select ASP.NET Core Web Application and Next.
  4. Provide a Project name and confirm or change the Location. Select Create.
  5. Select the latest version of ASP.NET Core in the drop-down (ASP.NET Core 5.0, and then select Web Application.
  6. Under Authentication, select Change and set the authentication to Individual User Accounts. Select OK.
  7. In the Create a new ASP.NET Core Web Application window, select Create.


The first install Authentication Google package from  Nuget package into our .NET 5 application. Open up Package Manager Console in Visual Studio and run the following command:

Install-Package Microsoft.AspNetCore.Authentication.Google

Install package more details https://www.nuget.org/packages/Microsoft.AspNetCore.Authentication.Google

or 

you can also install Authentication.Google package like as give below.



Modifying Login action and Handling Callback

The first is a GoogleLogin method which will request authentication and send the user of to Google. This will route to /account/google-login, which is what we set up as the LoginPath in our AddCookie method inside Startup.cs class.

using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using System.Security.Claims;
using System.Threading.Tasks;
namespace GoogleAuthor
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }
        public IConfiguration Configuration { get; }
        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.AddDbContext<DataContext>(
              options => options.UseSqlServer("name=ConnectionStrings:DefaultConnection"));
            //services.AddIdentity<ApplicationUser,Role>().AddEntityFrameworkStores<DbContext>();
            services.AddIdentity<IdentityUser, IdentityRole>()
               .AddEntityFrameworkStores<DataContext>()
               .AddDefaultTokenProviders();
            services.AddAuthentication()
         .AddGoogle(options =>
         {
             IConfigurationSection googleAuthNSection =
                   Configuration.GetSection("Authentication:Google");
             options.ClientId = "xxxxxxxxxxxxxxxxxx";
             options.ClientSecret = "xxxxxxxxxxxxxxxxxxxxxxxxxx";
               //this function is get user google profile image
               options.Scope.Add("profile");
             options.Events.OnCreatingTicket = (context) =>
             {
                 var picture = context.User.GetProperty("picture").GetString();
                 context.Identity.AddClaim(new Claim("picture", picture));
                 return Task.CompletedTask;
             };

         });
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });
            services.AddSession(options =>
            {
                options.Cookie.SameSite = SameSiteMode.None;
                options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
                options.Cookie.IsEssential = true;
            });
            services.AddControllersWithViews();
        }
        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.                 app.UseHsts();
            }
            app.UseStaticFiles();
            app.UseRouting();
            app.UseCors();
            app.UseAuthentication();
            app.UseAuthorization();
            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(                     name: "default",
                    pattern: "{controller=Account}/{action=Login}/{id?}");
            });
        }
    }
}

Open your AccountController.cs and past this code as given below.

External Identity Provider Implementation

The form the _ExternalAuthentication partial view alreadt targets the ExternalLogin action. So we have to create it and add the required logic:

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public IActionResult ExternalLogin(string provider, string returnUrl = null)
        {
            // Request a redirect to the external login provider.
            var redirectUrl = Url.Action(nameof(ExternalLoginCallback), "Account", new { returnUrl });             var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
            return Challenge(properties, provider);
        }

This is the action that we target by clicking the google button. It has two parameters: provider and returnUrl.

Additional Implementation

So, let’s create a new action and add the required logic:

        [HttpGet]
        [AllowAnonymous]
        public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
        {
            if (remoteError != null)
            {
                ErrorMessage = $"Error from external provider: {remoteError}";
                return RedirectToAction(nameof(Login));
            }
            var info = await _signInManager.GetExternalLoginInfoAsync();
            if (info == null)
            {
                return RedirectToAction(nameof(Login));
            }
            // Sign in the user with this external login provider if the user already has a login.
            var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);             if (result.Succeeded)
            {
                return Redirect(returnUrl);
                //_logger.LogInformation("User logged in with {Name} provider.", info.LoginProvider);
                //return RedirectToAction(nameof(returnUrl));
            }
            if (result.IsLockedOut)
            {                 return RedirectToAction(nameof(Lockout));
            }
            else
            { //get google login user infromation like that.
                if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
                {
                    string email = info.Principal.FindFirstValue(ClaimTypes.Email);
                }
                if (info.Principal.HasClaim(c => c.Type == ClaimTypes.GivenName))
                {
                    string firstName = info.Principal.FindFirstValue(ClaimTypes.GivenName);
                }
                if (info.Principal.HasClaim(c => c.Type == ClaimTypes.GivenName))
                {
                    string lastName = info.Principal.FindFirstValue(ClaimTypes.Surname);
                }
                if (info.Principal.HasClaim(c => c.Type == "picture"))
                {
                    string profileImage = info.Principal.FindFirstValue("picture");
                }
                var user = new ApplicationUser { UserName = model.Email, Email = model.Email, CreateDate = DateTime.UtcNow, EmailConfirmed = true };                 var result2 = await _userManager.CreateAsync(user);
                if (result2.Succeeded)
                {                     result2 = await _userManager.AddLoginAsync(user, info);
                    if (result2.Succeeded)
                    {
                           // do something here
                    }
                }
                return View("Error");
            }
        }

With the GetExternalLoginInfoAsync method, we collect exactly that – external login info. So the information like provider, given name, last name, email, name identifier, etc, are going to be provided in the info variable

AccountController.cs full code like as:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
namespace GoogleAuthor.Controllers
{
    public class AccountController : Controller
    {
        private readonly UserManager<IdentityUser> _userManager;
        private readonly SignInManager<IdentityUser> _signInManager;
        public AccountController(
         UserManager<IdentityUser> userManager,
         SignInManager<IdentityUser> signInManager
            )
        {
            _userManager = userManager;
            _signInManager = signInManager;
        }
        private ActionResult RedirectToLocal(string returnUrl)
        {
            return Redirect(returnUrl);
        }
        public IActionResult Login(string returnUrl)
        {
            return View();
        }
        [HttpPost]
        public IActionResult ExternalLogin( string returnUrl = null)
        {
            // Request a redirect to the external login provider.
            string provider = "Google";
            var redirectUrl = Url.Action(nameof(ExternalLoginCallback), "Account", new { returnUrl });
            var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
            return Challenge(properties, provider);
        }
        [TempData]
        public string ErrorMessage { get; set; }
        [HttpGet]
        [AllowAnonymous]
        public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
        {
            if (remoteError != null)
            {
               ErrorMessage = $"Error from external provider: {remoteError}";
                return RedirectToAction(nameof(ExternalLogin));
            }
            var info = await _signInManager.GetExternalLoginInfoAsync();
            if (info == null)
            {
                return RedirectToAction(nameof(ExternalLogin));
            }
            // Sign in the user with this external login provider if the user already has a login.
            var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);             if (result.Succeeded)
            {
                return RedirectToAction("Index", "Home");
                //_logger.LogInformation("User logged in with {Name} provider.", info.LoginProvider);
                //return RedirectToAction(nameof(returnUrl));
            }
            else
            {
                string email = string.Empty;
                string firstName = string.Empty;
                string lastName = string.Empty;
                string profileImage = string.Empty;
                //get google login user infromation like that.
                if (info.Principal.HasClaim(c => c.Type == ClaimTypes.Email))
                {                     email = info.Principal.FindFirstValue(ClaimTypes.Email);
                }
                if (info.Principal.HasClaim(c => c.Type == ClaimTypes.GivenName))
                {                     firstName = info.Principal.FindFirstValue(ClaimTypes.GivenName);
                }
                if (info.Principal.HasClaim(c => c.Type == ClaimTypes.GivenName))
                {
                    lastName = info.Principal.FindFirstValue(ClaimTypes.Surname);
                }
                if (info.Principal.HasClaim(c => c.Type == "picture"))
                {
                    profileImage = info.Principal.FindFirstValue("picture");
                }
                var user = new IdentityUser { UserName = email, Email = email, EmailConfirmed = true };
                var result2 = await _userManager.CreateAsync(user);
                if (result2.Succeeded)
                {
                    result2 = await _userManager.AddLoginAsync(user, info);
                    if (result2.Succeeded)
                    {
                        //do somethng here
                    }
                }
                return View("Login");
            }
        }
        public IActionResult GoogleLogInSuccess()
        {
            return View();
        }
    }
}


Then, we have to create a login view page after that past this like as give below.

@using Microsoft.AspNetCore.Identity;
@inject SignInManager<IdentityUser> SignInManager;
@{
    ViewData["Title"] = "Login";
    Layout = "~/Views/Shared/_Layout.cshtml";
}
<div class="row">
    <div class="col-sm-12">
        <div class="justify-content-center">
            <form asp-action="ExternalLogin" asp-controller="Account">
                <h4 class="text-center margin-top">Login With Google Account</h4>
                <hr />
                @Html.ValidationSummary(true, "", new { @class = "text-danger" })
                <input type="text" hidden="hidden" name="returnUrl" value="/Home/Index" class="currenctURL1" id="currentURL1" />
                <input type="submit" value="Login With Google" class="btn btn-success" />
            </form>
        </div>
    </div></div>


Related information

Sundar  Neupane

Sundar Neupane

I like working on projects with a team that cares about creating beautiful and usable interfaces.

If findandsolve.com felt valuable to you, feel free to share it.

Comments



Report Response