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
-
Go to the Google Cloud Console.
-
Create a new project (or select an existing one).
-
Navigate to APIs & Services > Credentials.
-
Click Create Credentials > OAuth client ID.
-
Choose Web application as the application type.
-
Set the Authorized redirect URI to:
https://localhost:5001/signin-google
-
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
-
Click New OAuth App.
-
Fill in details:
-
Application name:
AspNetOAuthLogin
-
Homepage URL:
https://localhost:5001/
-
Authorization callback URL:
https://localhost:5001/signin-github
-
-
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:
- ANGULAR and ASP. NET Core REST API - Real World Application
- Creating GraphQL APIs with ASP. Net Core for Beginners
- ASP .Net MVC Quick Start
- Master SignalR: Build Real-Time Web Apps with ASP. NET
- Fullstack Asp. Net Core MVC & C# Bootcamp With Real Projects
- ASP. NET Core MVC - A Step by Step Course
Thanks!