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

Google external login setup in ASP .NET Core(.NET 5 )

Google external login setup in ASP .NET Core(.NET 5 )

This article demonstrates how to build an .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 are log 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 .NET 5 app using a Google account.

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>


Selena  Kandakar

Selena Kandakar

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

Comments



Report Response