ASP.NET Core External Login Providers Google Step by step Implementation and Top 10 Questions and Answers
 Last Update: April 01, 2025      14 mins read      Difficulty-Level: beginner

Step-by-Step Guide to Implementing Google External Login in ASP.NET Core

Introduction

In today's digital age, users expect quick and seamless login experiences. External login providers such as Google, Facebook, and Twitter enable users to sign in using their existing accounts, reducing the friction associated with creating a new account on your platform. This guide will walk you through the process of integrating Google External Login into an ASP.NET Core application.

Prerequisites

Before we begin, ensure you have the following prerequisites:

  • .NET SDK 5.0 or later installed.
  • Visual Studio or any code editor (e.g., Visual Studio Code, JetBrains Rider).
  • Basic understanding of C# programming and ASP.NET Core principles.

Step 1: Create a New ASP.NET Core Web Application

  1. Start Visual Studio and choose Create a new project.
  2. Select ASP.NET Core Web App (Model-View-Controller) and click Next.
  3. Configure your project settings:
    • Name the project GoogleAuthDemo.
    • Choose a location to store the project.
    • Click Create.
  4. Select the ASP.NET Core version (preferably 5.0 or later).
  5. Check the Authentication option and set it to Individual User Accounts.
  6. Click Create to generate a new MVC project.

Step 2: Register Your Application in Google APIs Console

Google requires you to register your application to obtain the necessary credentials for authentication.

  1. Go to the Google Developers Console.
  2. Create a new project by clicking on Select a project and then New Project.
  3. Name your project, for example, GoogleAuthDemo, and click Create.
  4. Once the project is created, navigate to APIs & Services > Credentials.
  5. Click Create Credentials, then select OAuth client ID.
  6. Configure the consent screen if prompted:
    • Fill in the App name.
    • Provide the Support email.
    • Save and continue.
  7. Set up the OAuth Consent Screen:
    • Select External or Internal users as per your requirement.
    • Fill in the necessary information such as App domain, Authorized domains, and Application Home page.
    • Save and continue.
  8. Configure the credentials:
    • Choose Web application as the application type.
    • Name your credentials, e.g., GoogleAuthDemo.
    • Add Authorized redirect URIs. This must match the callback URL for your application. For development purposes, you can use https://localhost:5001/signin-google (adjust the port number accordingly).
    • Click Create.
  9. Copy the Client ID and Client Secret that are displayed. You will need these to configure your ASP.NET Core application.

Step 3: Configure Google External Authentication in ASP.NET Core

  1. Open your newly created ASP.NET Core project in Visual Studio.
  2. Open the appsettings.json file and add the Google client ID and client secret:
{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=aspnet-GoogleAuthDemo-879B329A-8955-406F-A372-8689B307D42A;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft": "Warning",
      "Microsoft.Hosting.Lifetime": "Information"
    }
  },
  "AllowedHosts": "*",
  "Authentication": {
    "Google": {
      "ClientId": "YOUR_GOOGLE_CLIENT_ID",
      "ClientSecret": "YOUR_GOOGLE_CLIENT_SECRET"
    }
  }
}

Replace YOUR_GOOGLE_CLIENT_ID and YOUR_GOOGLE_CLIENT_SECRET with the values obtained from the Google Developers Console.

  1. Open Startup.cs or Program.cs depending on your ASP.NET Core version.

    • For ASP.NET Core 6.0 and later, you’ll be using Program.cs. For earlier versions like 3.x and 5.x, you'll be using Startup.cs. Instructions below cater to both versions.
  2. For ASP.NET Core 6.0 and later (Program.cs):

using Microsoft.AspNetCore.Authentication.Google;
using Microsoft.AspNetCore.Authentication.JwtBearer;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.
var connectionString = builder.Configuration.GetConnectionString("DefaultConnection") ?? throw new InvalidOperationException("Connection string 'DefaultConnection' not found.");
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(connectionString));
builder.Services.AddDatabaseDeveloperPageExceptionFilter();

builder.Services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
    .AddEntityFrameworkStores<ApplicationDbContext>();

builder.Services.AddControllersWithViews();

// Add Google Authentication
builder.Services.AddAuthentication()
    .AddGoogle(options =>
    {
        IConfigurationSection googleAuthNSection =
            builder.Configuration.GetSection("Authentication:Google");

        options.ClientId = googleAuthNSection["ClientId"];
        options.ClientSecret = googleAuthNSection["ClientSecret"];
    });

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseMigrationsEndPoint();
}
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.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication();
app.UseAuthorization();

app.MapControllerRoute(
    name: "default",
    pattern: "{controller=Home}/{action=Index}/{id?}");
app.MapRazorPages();

app.Run();
  1. For earlier versions of ASP.NET Core (using Startup.cs):

    • In Startup.cs, find the ConfigureServices method and add Google authentication.
public void ConfigureServices(IServiceCollection services)
{
    services.AddDbContext<ApplicationDbContext>(options =>
        options.UseSqlServer(
            Configuration.GetConnectionString("DefaultConnection")));

    services.AddDatabaseDeveloperPageExceptionFilter();

    services.AddDefaultIdentity<IdentityUser>(options => options.SignIn.RequireConfirmedAccount = true)
        .AddEntityFrameworkStores<ApplicationDbContext>();

    services.AddControllersWithViews();

    // Add Google Authentication
    services.AddAuthentication().AddGoogle(options =>
    {
        IConfigurationSection googleAuthNSection =
            Configuration.GetSection("Authentication:Google");

        options.ClientId = googleAuthNSection["ClientId"];
        options.ClientSecret = googleAuthNSection["ClientSecret"];
    });
}
  1. For both versions: Ensure that app.UseAuthentication() and app.UseAuthorization() are called in the Configure method within Startup.cs or Program.cs.

Step 4: Update the Account Controller and Views

The default ASP.NET Core Identity setup already includes support for external logins. However, you need to update the views to include the Google login option.

  1. Open the Login.cshtml view located in Views/Account.

  2. Add the following code after the Username/Password form:

<h4>Use a third-party account to log in.</h4>
<hr />
<div>
    <p>
        @foreach (var provider in Model.ExternalLogins!)
        {
            <form asp-page="./ExternalLogin" asp-route-returnUrl="@Model.ReturnUrl" method="post" class="form-horizontal">
                <button type="submit" class="btn btn-primary" name="provider" value="@provider.Name" title="Log in using your @provider.DisplayName account">@provider.DisplayName</button>
            </form>
        }
    </p>
</div>
  1. Update the Login.cshtml.cs model to pass the external logins to the view:
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using Microsoft.Extensions.Logging;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using GoogleAuthDemo.Data;

namespace GoogleAuthDemo.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class LoginModel : PageModel
    {
        private readonly UserManager<IdentityUser> _userManager;
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly ILogger<LoginModel> _logger;

        public LoginModel(SignInManager<IdentityUser> signInManager,
            ILogger<LoginModel> logger,
            UserManager<IdentityUser> userManager)
        {
            _userManager = userManager;
            _signInManager = signInManager;
            _logger = logger;
        }

        [BindProperty]
        public InputModel Input { get; set; } = new InputModel();

        public IList<AuthenticationScheme> ExternalLogins { get; set; } = new List<AuthenticationScheme>();

        public string ReturnUrl { get; set; } = string.Empty;

        [TempData]
        public string ErrorMessage { get; set; } = string.Empty;

        public class InputModel
        {
            [Required]
            [EmailAddress]
            public string Email { get; set; } = string.Empty;

            [Required]
            [DataType(DataType.Password)]
            public string Password { get; set; } = string.Empty;

            [Display(Name = "Remember me?")]
            public bool RememberMe { get; set; }
        }

        public async Task OnGetAsync(string returnUrl = null)
        {
            if (!string.IsNullOrEmpty(ErrorMessage))
            {
                ModelState.AddModelError(string.Empty, ErrorMessage);
            }

            returnUrl ??= Url.Content("~/");

            // Clear the existing external cookie to ensure a clean login process
            await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();

            ReturnUrl = returnUrl;
        }

        public async Task<IActionResult> OnPostAsync(string returnUrl = null)
        {
            returnUrl ??= Url.Content("~/");

            ExternalLogins = (await _signInManager.GetExternalAuthenticationSchemesAsync()).ToList();

            if (ModelState.IsValid)
            {
                // This doesn't count login failures towards account lockout
                // To enable password failures to trigger account lockout, set lockoutOnFailure: true
                var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
                if (result.Succeeded)
                {
                    _logger.LogInformation("User logged in.");
                    return LocalRedirect(returnUrl);
                }
                if (result.RequiresTwoFactor)
                {
                    return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
                }
                if (result.IsLockedOut)
                {
                    _logger.LogWarning("User account locked out.");
                    return RedirectToPage("./Lockout");
                }
                else
                {
                    ModelState.AddModelError(string.Empty, "Invalid login attempt.");
                    return Page();
                }
            }

            // If we got this far, something failed, redisplay form
            return Page();
        }
    }
}

Step 5: Test the Google External Login

  1. Run your application. You can do this by pressing F5 or clicking on the Start Debugging button in Visual Studio.
  2. Navigate to the Login page (https://localhost:5001/Identity/Account/Login).
  3. You should see an option to log in with Google.
  4. Click on the Google button.
  5. You will be redirected to the Google login page. Enter your Google credentials.
  6. After successful authentication, you will be redirected back to your application and logged in.

Step 6: Handle External Login Registration

When a user logs in with an external provider, they might not have an account in your application. You can handle this by redirecting the user to a registration page to complete the registration process.

  1. Modify the ExternalLogin page model (Pages\Account\ExternalLogin.cshtml.cs) to handle new user registrations:
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using GoogleAuthDemo.Data;

namespace GoogleAuthDemo.Areas.Identity.Pages.Account
{
    [AllowAnonymous]
    public class ExternalLoginModel : PageModel
    {
        private readonly SignInManager<IdentityUser> _signInManager;
        private readonly UserManager<IdentityUser> _userManager;
        private readonly ILogger<ExternalLoginModel> _logger;

        public ExternalLoginModel(
            SignInManager<IdentityUser> signInManager,
            UserManager<IdentityUser> userManager,
            ILogger<ExternalLoginModel> logger)
        {
            _signInManager = signInManager;
            _userManager = userManager;
            _logger = logger;
        }

        [BindProperty]
        public InputModel Input { get; set; } = new InputModel();

        public string LoginProvider { get; set; } = string.Empty;

        public string ReturnUrl { get; set; } = string.Empty;

        [TempData]
        public string ErrorMessage { get; set; } = string.Empty;

        public string Email { get; set; } = string.Empty;

        public class InputModel
        {
            [Required]
            [EmailAddress]
            public string Email { get; set; } = string.Empty;
        }

        public IActionResult OnGet()
        {
            return RedirectToPage("./Login");
        }

        public async Task<IActionResult> OnPost(string provider, string returnUrl = null)
        {
            // Request a redirect to the external login provider.
            var redirectUrl = Url.Page("./ExternalLogin", pageHandler: "Callback", values: new { area = "Identity", returnUrl });
            var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
            return new ChallengeResult(provider, properties);
        }

        public async Task<IActionResult> OnGetCallbackAsync(string returnUrl = null, string remoteError = null)
        {
            returnUrl = returnUrl ?? Url.Content("~/");
            if (remoteError != null)
            {
                ErrorMessage = $"Error from external provider: {remoteError}";
                return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
            }
            var info = await _signInManager.GetExternalLoginInfoAsync();
            if (info == null)
            {
                ErrorMessage = "Error loading external login information.";
                return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
            }

            // 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)
            {
                _logger.LogInformation("{Name} logged in with {LoginProvider} provider.", info.Principal.Identity.Name, info.LoginProvider);
                return LocalRedirect(returnUrl);
            }
            if (result.IsLockedOut)
            {
                return RedirectToPage("./Lockout");
            }
            else
            {
                // If the user does not have an account, then ask the user to create an account.
                ReturnUrl = returnUrl;
                LoginProvider = info.LoginProvider;
                Email = info.Principal.FindFirstValue("email");
                Input.Email = Email;
                return Page();
            }
        }

        public async Task<IActionResult> OnPostConfirmationAsync(string returnUrl = null)
        {
            returnUrl = returnUrl ?? Url.Content("~/");
            // Get the information about the user from the external login provider
            var info = await _signInManager.GetExternalLoginInfoAsync();
            if (info == null)
            {
                ErrorMessage = "Error loading external login information during confirmation.";
                return RedirectToPage("./Login", new { ReturnUrl = returnUrl });
            }

            if (ModelState.IsValid)
            {
                var user = new IdentityUser { UserName = Input.Email, Email = Input.Email };
                var result = await _userManager.CreateAsync(user);
                if (result.Succeeded)
                {
                    result = await _userManager.AddLoginAsync(user, info);
                    if (result.Succeeded)
                    {
                        await _signInManager.SignInAsync(user, isPersistent: false);
                        _logger.LogInformation("User created an account using {Name} provider.", info.LoginProvider);
                        return LocalRedirect(returnUrl);
                    }
                }
                ForEach(err => ModelState.AddModelError(string.Empty, err.Description));
            }

            LoginProvider = info.LoginProvider;
            ReturnUrl = returnUrl;
            return Page();
        }
    }
}
  1. Create the ExternalLogin.cshtml page (Pages\Account\ExternalLogin.cshtml) to allow users to register after external login:
@page
@model ExternalLoginModel
@{
    ViewData["Title"] = "Register";
}

<h2>@ViewData["Title"] your @Model.LoginProvider account.</h2>
<h4 id="external-login-title">Associate your @Model.LoginProvider account.</h4>
<hr />

<div asp-validation-summary="ModelOnly" class="text-danger"></div>

<p id="external-login-description">
    You've successfully authenticated with <strong>@Model.LoginProvider</strong>.
    Please enter an email address for this site below and click the Register button to finish
    logging in.
</p>

<div class="row">
    <div class="col-md-4">
        <form asp-page-handler="Confirmation" asp-route-returnUrl="@Model.ReturnUrl" method="post">
            <div asp-validation-summary="ModelOnly" class="text-danger"></div>
            <div class="form-floating">
                <input asp-for="Input.Email" class="form-control" autocomplete="email" />
                <label asp-for="Input.Email" class="form-label"></label>
                <span asp-validation-for="Input.Email" class="text-danger"></span>
            </div>
            <button type="submit" class="w-100 btn btn-lg btn-primary">Register</button>
        </form>
    </div>
</div>

@section Scripts {
    <partial name="_ValidationScriptsPartial" />
}

Step 7: Secure Your Application for Production

When deploying your application to production, ensure that you follow best practices:

  1. Use HTTPS: Ensure that your application is served over HTTPS to protect sensitive data, including Google Client ID and Client Secret.
  2. Validate Redirect URIs: Double-check the redirect URIs in the Google Developers Console to ensure they match your production URLs.
  3. Environment Variables: Store sensitive information such as Client ID and Client Secret in environment variables or secure vaults rather than hardcoding them in your configuration files.
  4. Rate Limiting and Throttling: Implement rate limiting to prevent abuse of your login system.

Conclusion

Congratulations! You’ve successfully integrated Google External Login into your ASP.NET Core application. This allows users to authenticate using their Google accounts, providing a seamless and efficient login experience. You can further customize the authentication flow by handling additional claims, integrating other external providers, and enhancing security measures.

Feel free to explore the ASP.NET Core documentation for more advanced scenarios and configurations. Happy coding!