OAuth2 Login in ASP.NET Core 8 with Google and GitHub

by Didin J. on Aug 15, 2025 OAuth2 Login in ASP.NET Core 8 with Google and GitHub

Learn how to integrate Google and GitHub OAuth2 login in ASP.NET Core 8 with custom user model, role-based access control, and a simple Razor Pages UI.

Authentication is a critical part of almost every modern web application. Instead of building your own username and password system, many developers rely on OAuth2 login with external providers such as Google and GitHub. This approach not only improves security but also simplifies the login experience for users by letting them use accounts they already trust.

In this tutorial, we’ll build an ASP.NET Core 8 web application that supports authentication using both Google and GitHub. By the end, you’ll have a working project where users can log in securely with either provider, and you’ll know how to extend it further with ASP.NET Identity and custom claims.

Prerequisites

Before we begin, make sure you have:

  • .NET 8 SDK installed (download from dotnet.microsoft.com).

  • Basic knowledge of ASP.NET Core MVC or Razor Pages.

  • A Google Cloud project with OAuth 2.0 credentials was created.

  • A GitHub OAuth application registered in your GitHub account.

  • An IDE or editor such as Visual Studio 2022 or Visual Studio Code.


Set up a New ASP.NET Core 8 Project

Open your terminal or command prompt, then create a new Razor Pages project:

dotnet new webapp -o AspNetOAuthLogin
cd AspNetOAuthLogin

This command creates a new ASP.NET Core 8 web application with Razor Pages enabled.

Next, open the project in your editor. For example, in VS Code:

code .

At this point, we have a clean ASP.NET Core project that we’ll enhance with Google and GitHub OAuth2 login.


Configure Google OAuth2 Login

1. Create OAuth Credentials in Google Cloud

  1. Go to the Google Cloud Console.

  2. Create a new project (or select an existing one).

  3. Navigate to APIs & Services > Credentials.

  4. Click Create Credentials > OAuth client ID.

  5. Choose Web application as the application type.

  6. Set the Authorized redirect URI to:

    https://localhost:5001/signin-google
  7. Save and note the Client ID and Client Secret.

2. Update appsettings.json

Open the appsettings.json file and add your Google OAuth credentials:

{
  "Authentication": {
    "Google": {
      "ClientId": "YOUR_GOOGLE_CLIENT_ID",
      "ClientSecret": "YOUR_GOOGLE_CLIENT_SECRET"
    }
  }
}

3. Configure Google Authentication in Program.cs

Install the Microsoft.AspNetCore.Authentication.Google package.

dotnet add package Microsoft.AspNetCore.Authentication.Google --version 8.0.8

Open the Program.cs file and update the authentication configuration:

var builder = WebApplication.CreateBuilder(args);

// Add services to the container
builder.Services.AddRazorPages();
builder.Services.AddAuthentication()
    .AddGoogle(options =>
    {
        options.ClientId = builder.Configuration["Authentication:Google:ClientId"];
        options.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
    });

var app = builder.Build();

// Configure the HTTP request pipeline
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Error");
    app.UseHsts();
}

app.UseHttpsRedirection();
app.UseStaticFiles();

app.UseRouting();

app.UseAuthentication(); // Enable authentication
app.UseAuthorization();

app.MapRazorPages();

app.Run();

4. Add Login Button

Open the Pages/Shared/_Layout.cshtml file and add a login link inside the navigation bar:

<li class="nav-item">
    <a class="nav-link text-dark" asp-area="" asp-page="/Account/Login">Login with Google</a>
</li>

At this point, your app is configured to allow Google login. When users click the login link, they’ll be redirected to Google’s OAuth2 consent screen and back to your app upon successful authentication.


Configure GitHub OAuth2 Login

1. Register a GitHub OAuth App

  1. Go to GitHub Developer Settings → OAuth Apps.

  2. Click New OAuth App.

  3. Fill in details:

    • Application name: AspNetOAuthLogin

    • Homepage URL: https://localhost:5001/

    • Authorization callback URL:

      https://localhost:5001/signin-github
  4. Save and copy the Client ID and Client Secret.

2. Install GitHub OAuth Package

For GitHub login, install the AspNet.Security.OAuth.GitHub package (works with .NET 8):

dotnet add package AspNet.Security.OAuth.GitHub --version 8.*

Example:

dotnet add package AspNet.Security.OAuth.GitHub --version 8.0.0

3. Update appsettings.json

Add your GitHub OAuth credentials:

{
  "Authentication": {
    "Google": {
      "ClientId": "YOUR_GOOGLE_CLIENT_ID",
      "ClientSecret": "YOUR_GOOGLE_CLIENT_SECRET"
    },
    "GitHub": {
      "ClientId": "YOUR_GITHUB_CLIENT_ID",
      "ClientSecret": "YOUR_GITHUB_CLIENT_SECRET"
    }
  }
}

4. Configure GitHub Authentication in Program.cs

At the top of Program.cs, add:

using Microsoft.AspNetCore.Authentication;
using AspNet.Security.OAuth.GitHub;

Then extend your authentication configuration:

builder.Services.AddAuthentication()
    .AddGoogle(options =>
    {
        options.ClientId = builder.Configuration["Authentication:Google:ClientId"];
        options.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
    })
    .AddGitHub(options =>
    {
        options.ClientId = builder.Configuration["Authentication:GitHub:ClientId"];
        options.ClientSecret = builder.Configuration["Authentication:GitHub:ClientSecret"];
    });

5. Add GitHub Login Button

In Pages/Shared/_Layout.cshtml, add another login link:

<li class="nav-item">
    <a class="nav-link text-dark" asp-area="" asp-page="/Account/Login">Login with Google</a>
</li>
<li class="nav-item">
    <a class="nav-link text-dark" href="/signin-github">Login with GitHub</a>
</li>

6. Run and Test

Run your app:

dotnet run
  • Click Login with Google → should redirect to the Google consent screen.

  • Click Login with GitHub → should redirect to GitHub login.

Once authenticated, the user is redirected back to your app with their profile info.


Add ASP.NET Core Identity

1. Install Required Packages

Run the following commands to add Identity and EF Core with SQL Server (you can switch to another database if you prefer):

dotnet add package Microsoft.AspNetCore.Identity.EntityFrameworkCore --version 8.0.8
dotnet add package Microsoft.EntityFrameworkCore.SqlServer --version 8.0.8
dotnet add package Microsoft.EntityFrameworkCore.Tools --version 8.0.8
dotnet add package Microsoft.AspNetCore.Identity.UI --version 8.0.8

2. Create the ApplicationDbContext

In a new folder Data, create ApplicationDbContext.cs:

using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace AspNetOAuthLogin.Data
{
    public class ApplicationDbContext(DbContextOptions<ApplicationDbContext> options) : IdentityDbContext<IdentityUser>(options)
    {
    }
}

3. Configure Identity in Program.cs

Update your builder configuration:

using Microsoft.AspNetCore.Authentication;
using AspNet.Security.OAuth.GitHub;
using Microsoft.AspNetCore.Identity;
using Microsoft.EntityFrameworkCore;
using AspNetOAuthLogin.Data;

var builder = WebApplication.CreateBuilder(args);

// Add EF Core with SQL Server
builder.Services.AddDbContext<ApplicationDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

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

// Add services to the container.
builder.Services.AddAuthentication()
    .AddGoogle(options =>
    {
        options.ClientId = builder.Configuration["Authentication:Google:ClientId"];
        options.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
    })
    .AddGitHub(options =>
    {
        options.ClientId = builder.Configuration["Authentication:GitHub:ClientId"];
        options.ClientSecret = builder.Configuration["Authentication:GitHub:ClientSecret"];
    });

var app = builder.Build();

// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/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(); // Enable authentication
app.UseAuthorization();

app.MapRazorPages();

app.Run();

4. Update appsettings.json

Add your database connection string:

{
  "ConnectionStrings": {
    "DefaultConnection": "Server=(localdb)\\MSSQLLocalDB;Database=AspNetOAuthLoginDb;Trusted_Connection=True;MultipleActiveResultSets=true"
  },
  "Authentication": {
    "Google": {
      "ClientId": "YOUR_GOOGLE_CLIENT_ID",
      "ClientSecret": "YOUR_GOOGLE_CLIENT_SECRET"
    },
    "GitHub": {
      "ClientId": "YOUR_GITHUB_CLIENT_ID",
      "ClientSecret": "YOUR_GITHUB_CLIENT_SECRET"
    }
  }
}

5. Run EF Core Migrations

dotnet ef migrations add InitialCreate
dotnet ef database update

This creates all required Identity tables (AspNetUsers, AspNetUserLogins, etc.).

6. Protect Pages with Authentication

Example: Require login for the Index page.
Open Pages/Index.cshtml.cs:

using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace AspNetOAuthLogin.Pages
{
    [Authorize]
    public class IndexModel : PageModel
    {
        public void OnGet()
        {
        }
    }
}

7. Update Navigation Links

In Pages/Shared/_Layout.cshtml, update login/logout:

@if (User.Identity.IsAuthenticated)
{
    <li class="nav-item">
        <form asp-area="" asp-page="/Account/Logout" method="post" id="logoutForm" class="form-inline">
            <button type="submit" class="btn btn-link nav-link text-dark">Logout</button>
        </form>
    </li>
}
else
{
    <li class="nav-item">
        <a class="nav-link text-dark" href="/signin-google">Login with Google</a>
    </li>
    <li class="nav-item">
        <a class="nav-link text-dark" href="/signin-github">Login with GitHub</a>
    </li>
}

✅ Now, when users log in with Google or GitHub:

  • Their account is automatically created in your local Identity DB.

  • Returning users are linked via their provider (AspNetUserLogins).

  • You can use Identity features (roles, claims, authorization policies).


Role-Based Access Control (Admin/User)

🔹 Step 1. Extend Identity to Support Roles

Update your Program.cs Identity registration to include roles:

builder.Services.AddDefaultIdentity<IdentityUser>(options =>
    options.SignIn.RequireConfirmedAccount = true)
    .AddRoles<IdentityRole>() // ✅ Add role support
    .AddEntityFrameworkStores<ApplicationDbContext>();

Now your app supports both users and roles.

🔹 Step 2. Seed Default Roles (Admin/User)

You need to create the roles at startup if they don’t exist. Add this helper method in Program.cs after building the app:

using Microsoft.AspNetCore.Identity;

async Task CreateRolesAndAdminUserAsync(WebApplication app)
{
    using var scope = app.Services.CreateScope();
    var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
    var userManager = scope.ServiceProvider.GetRequiredService<UserManager<IdentityUser>>();

    // Ensure roles exist
    string[] roles = { "Admin", "User" };
    foreach (var role in roles)
    {
        if (!await roleManager.RoleExistsAsync(role))
        {
            await roleManager.CreateAsync(new IdentityRole(role));
        }
    }

    // Optionally seed an Admin user (local email/password)
    var adminEmail = "[email protected]";
    var adminUser = await userManager.FindByEmailAsync(adminEmail);

    if (adminUser == null)
    {
        adminUser = new IdentityUser { UserName = adminEmail, Email = adminEmail, EmailConfirmed = true };
        var result = await userManager.CreateAsync(adminUser, "Admin@12345"); // strong password
        if (result.Succeeded)
        {
            await userManager.AddToRoleAsync(adminUser, "Admin");
        }
    }
}

Then call it at the bottom of Program.cs:

await CreateRolesAndAdminUserAsync(app);

🔹 Step 3. Handle Google/GitHub Login and Assign Role

When a user logs in via Google or GitHub, you can assign them a default role (User) or special role (Admin) based on their email.

Update your external login setup:

builder.Services.AddAuthentication()
    .AddGoogle(googleOptions =>
    {
        googleOptions.ClientId = builder.Configuration["Authentication:Google:ClientId"];
        googleOptions.ClientSecret = builder.Configuration["Authentication:Google:ClientSecret"];
    })
    .AddGitHub(githubOptions =>
    {
        githubOptions.ClientId = builder.Configuration["Authentication:GitHub:ClientId"];
        githubOptions.ClientSecret = builder.Configuration["Authentication:GitHub:ClientSecret"];
    });

Now add an event handler for assigning roles after login.
Create a custom Claims Transformation Service:

using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Identity;
using System.Security.Claims;

public class RoleClaimsTransformer : IClaimsTransformation
{
    private readonly UserManager<IdentityUser> _userManager;

    public RoleClaimsTransformer(UserManager<IdentityUser> userManager)
    {
        _userManager = userManager;
    }

    public async Task<ClaimsPrincipal> TransformAsync(ClaimsPrincipal principal)
    {
        var identity = (ClaimsIdentity)principal.Identity!;
        var user = await _userManager.GetUserAsync(principal);

        if (user != null)
        {
            var roles = await _userManager.GetRolesAsync(user);
            foreach (var role in roles)
            {
                if (!identity.Claims.Any(c => c.Type == ClaimTypes.Role && c.Value == role))
                {
                    identity.AddClaim(new Claim(ClaimTypes.Role, role));
                }
            }
        }

        return principal;
    }
}

Register this service in Program.cs:

builder.Services.AddScoped<IClaimsTransformation, RoleClaimsTransformer>();

🔹 Step 4. Assign Role Automatically for Google/GitHub Logins

In your Account/ExternalLogin callback controller (or middleware if you’re using Razor Pages):

var user = await _userManager.FindByEmailAsync(info.Principal.FindFirstValue(ClaimTypes.Email));
if (user != null && !await _userManager.IsInRoleAsync(user, "User"))
{
    await _userManager.AddToRoleAsync(user, "User");
}

Optionally, assign Admin role if the email matches your own:

if (user.Email == "[email protected]")
{
    if (!await _userManager.IsInRoleAsync(user, "Admin"))
    {
        await _userManager.AddToRoleAsync(user, "Admin");
    }
}

🔹 Step 5. Protect Routes by Role

Now you can secure controllers or Razor Pages:

[Authorize(Roles = "Admin")]
public IActionResult AdminOnly()
{
    return View();
}

[Authorize(Roles = "User")]
public IActionResult UserOnly()
{
    return View();
}

Or in Razor Pages:

@attribute [Authorize(Roles = "Admin")]

✅ Now your app supports:

  • Local Identity login with roles

  • Google + GitHub OAuth2 logins

  • Automatic role assignment (User by default, Admin for specific emails)

  • Role-based access control in controllers/pages


Simple Razor Pages UI

1. Create a Razor Pages UI

Create a folder Models in your project and add this file:

Models/ApplicationUser.cs

using Microsoft.AspNetCore.Identity;

namespace AspNetOAuthLogin.Models
{
    public class ApplicationUser : IdentityUser
    {
        // Custom properties if needed
        public string? Role { get; set; }
    }
}

Data/ApplicationDbContext.cs

using AspNetOAuthLogin.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore;

namespace AspNetOAuthLogin.Data
{
    public class ApplicationDbContext : IdentityDbContext<ApplicationUser>
    {
        public ApplicationDbContext(DbContextOptions<ApplicationDbContext> options)
            : base(options)
        {
        }
    }
}

In your project root, create a folder Pages. Add these Razor Pages:

Pages/Index.cshtml

@page
@using Microsoft.AspNetCore.Identity
@using AspNetOAuthLogin.Models
@inject SignInManager<ApplicationUser> SignInManager
@inject UserManager<ApplicationUser> UserManager

<h1>Welcome to OAuth2 Login Demo</h1>

@if (SignInManager.IsSignedIn(User))
{
    <p>Hello, @User.Identity?.Name!</p>

    @if (User.IsInRole("Admin"))
    {
        <p><strong>You are an Admin.</strong> You have access to manage users and settings.</p>
        <a asp-page="/Admin">Go to Admin Page</a>
    }
    else if (User.IsInRole("User"))
    {
        <p><strong>You are a User.</strong> You can view user-only content.</p>
        <a asp-page="/User">Go to User Page</a>
    }

    <form method="post" asp-page-handler="Logout">
        <button type="submit" class="btn btn-danger">Logout</button>
    </form>
}
else
{
    <p>Please log in:</p>
    <a asp-controller="Account" asp-action="ExternalLogin" asp-route-provider="Google">Login with Google</a><br />
    <a asp-controller="Account" asp-action="ExternalLogin" asp-route-provider="GitHub">Login with GitHub</a>
}

Pages/Admin.cshtml

@page
@attribute [Authorize(Roles = "Admin")]

<h2>Admin Dashboard</h2>
<p>Only users with the <strong>Admin</strong> role can access this page.</p>

Pages/User.cshtml

@page
@attribute [Authorize(Roles = "User")]

<h2>User Dashboard</h2>
<p>Only users with the <strong>User</strong> role can access this page.</p>

2. Add Role Assignment After Login

In Program.cs, after successful login, assign a default role if the user doesn’t have one yet.

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

app.MapControllers();
app.MapRazorPages();

// Seed Roles (Admin, User) at startup
using (var scope = app.Services.CreateScope())
{
    var roleManager = scope.ServiceProvider.GetRequiredService<RoleManager<IdentityRole>>();
    var userManager = scope.ServiceProvider.GetRequiredService<UserManager<ApplicationUser>>();

    string[] roles = { "Admin", "User" };
    foreach (var role in roles)
    {
        if (!await roleManager.RoleExistsAsync(role))
            await roleManager.CreateAsync(new IdentityRole(role));
    }

    // Optional: assign the first registered user as Admin
    var users = userManager.Users.ToList();
    if (users.Count == 1)
    {
        await userManager.AddToRoleAsync(users[0], "Admin");
    }
}

3. Update AccountController.cs to Assign Role at First Login

When a user logs in with Google/GitHub, assign them the User role by default if they don’t have one.

// Inside ExternalLoginCallback after user creation:
if (!await _userManager.IsInRoleAsync(user, "User"))
{
    await _userManager.AddToRoleAsync(user, "User");
}

✅ With this UI:

  • Logged-out users see Login with Google/GitHub buttons.

  • Logged-in users see a personalized greeting.

  • Users with the Admin role can access the Admin Dashboard.

  • Users with the User role can access the User Dashboard.


Conclusion

In this tutorial, we built a complete ASP.NET Core 8 OAuth Login system with external providers like Google, GitHub, and Microsoft. We extended the authentication by creating a custom ApplicationUser model, configured ApplicationDbContext, and integrated everything into Razor Pages. Finally, we added a simple UI to display user information once they are authenticated.

With this foundation, you can now:

  • Extend the ApplicationUser model with more profile fields.

  • Implement role-based authorization (e.g., Admin vs. User access).

  • Customize the UI with navigation links and secure pages.

  • Add multiple OAuth providers or even integrate JWT for APIs.

This approach gives you a secure, flexible authentication system ready for real-world applications.

You can get the full source code on our GitHub.

That's just the basics. If you need more deep learning about ASP.NET Core, Angular, or related, you can take the following cheap course:

Thanks!