Implementing Google OAuth in ASP.NET Web API
1. Setting Up Google OAuth Credentials
Before writing any code, you need to set up a project in the Google Cloud Console:
Go to the Google Cloud Console
Create a new project or select an existing one
Navigate to "APIs & Services" > "Credentials"
Click "Create Credentials" and select "OAuth client ID"
Configure the OAuth consent screen with your application details
For application type, select "Web application"
Add authorized JavaScript origins (e.g.,
http://localhost:7276
for development)Add authorized redirect URIs
Create the client ID and note your Client ID and Client Secret
2. Configuring Your ASP.NET Core Application
Add the necessary NuGet packages to your project:
dotnet add package Google.Apis.Auth
dotnet add package Microsoft.AspNetCore.Identity
dotnet add package Microsoft.AspNetCore.Authentication.JwtBearer
Update your appsettings.json
file with the Google OAuth configuration:
{
"Google": {
"ClientId": "YOUR_GOOGLE_CLIENT_ID",
"ClientSecret": "YOUR_GOOGLE_CLIENT_SECRET"
},
"JwtConfig": {
"Secret": "YOUR_SECURE_JWT_SECRET_KEY",
"Issuer": "YourAppName",
"Audience": "YourAppUsers",
"ExpiryInMinutes": 60
}
}
3. The Token Service Implementation
The token service is responsible for generating JWT tokens for authenticated users:
public class TokenService
{
private readonly IConfiguration _configuration;
private readonly UserManager<ApplicationUser> _userManager;
public TokenService(IConfiguration configuration, UserManager<ApplicationUser> userManager)
{
_configuration = configuration;
_userManager = userManager;
}
public async Task<string> GenerateToken(ApplicationUser user)
{
var jwtTokenHandler = new JwtSecurityTokenHandler();
var key = Encoding.ASCII.GetBytes(_configuration["JwtConfig:Secret"]);
var roles = await _userManager.GetRolesAsync(user);
// Create claims for the token
var claims = new List<Claim>
{
new Claim(ClaimTypes.NameIdentifier, user.Id),
new Claim(JwtRegisteredClaimNames.Sub, user.UserName),
new Claim(JwtRegisteredClaimNames.Email, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())
};
// Add role claims
foreach (var role in roles)
{
claims.Add(new Claim(ClaimTypes.Role, role));
}
// Create token descriptor
var now = DateTime.UtcNow;
var expires = now.AddMinutes(Convert.ToDouble(_configuration["JwtConfig:ExpiryInMinutes"] ?? "60"));
var tokenDescriptor = new SecurityTokenDescriptor
{
Subject = new ClaimsIdentity(claims),
NotBefore = now,
Expires = expires,
SigningCredentials = new SigningCredentials(
new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature),
Issuer = _configuration["JwtConfig:Issuer"] ?? "DefaultIssuer",
Audience = _configuration["JwtConfig:Audience"] ?? "DefaultAudience"
};
var token = jwtTokenHandler.CreateToken(tokenDescriptor);
return jwtTokenHandler.WriteToken(token);
}
}
4. The Authentication Controller
Next, implement the controller that handles Google authentication:
[Route("api/[controller]")]
[ApiController]
public class AuthController : ControllerBase
{
private readonly UserManager<ApplicationUser> _userManager;
private readonly RoleManager<IdentityRole> _roleManager;
private readonly IConfiguration _configuration;
private readonly TokenService _tokenService;
public AuthController(
UserManager<ApplicationUser> userManager,
RoleManager<IdentityRole> roleManager,
IConfiguration configuration,
TokenService tokenService)
{
_userManager = userManager;
_roleManager = roleManager;
_configuration = configuration;
_tokenService = tokenService;
}
[HttpPost("google-login")]
public async Task<IActionResult> GoogleLogin([FromBody] GoogleLoginDTO model)
{
// Validate request
if (string.IsNullOrEmpty(model.IdToken))
{
return BadRequest(new AuthResponseDto
{
IsSuccess = false,
Message = "Google token is required"
});
}
try
{
// Validate Google token
var payload = await GoogleJsonWebSignature.ValidateAsync(
model.IdToken,
new GoogleJsonWebSignature.ValidationSettings
{
Audience = new[] { _configuration["Google:ClientId"] }
});
// Check if user exists
var user = await _userManager.FindByEmailAsync(payload.Email);
if (user == null)
{
// Create new user if doesn't exist
user = new ApplicationUser
{
UserName = payload.Email,
Email = payload.Email,
EmailConfirmed = true,
SecurityStamp = Guid.NewGuid().ToString()
};
var createResult = await _userManager.CreateAsync(user);
if (!createResult.Succeeded)
{
return BadRequest(new AuthResponseDto
{
IsSuccess = false,
Message = "User creation failed: " +
string.Join(", ", createResult.Errors.Select(e => e.Description))
});
}
// Ensure Customer role exists
if (!await _roleManager.RoleExistsAsync("Customer"))
{
await _roleManager.CreateAsync(new IdentityRole("Customer"));
}
// Add to Customer role
var roleResult = await _userManager.AddToRoleAsync(user, "Customer");
if (!roleResult.Succeeded)
{
return BadRequest(new AuthResponseDto
{
IsSuccess = false,
Message = "Failed to assign role: " +
string.Join(", ", roleResult.Errors.Select(e => e.Description))
});
}
}
// Generate JWT token
var token = await _tokenService.GenerateToken(user);
var roles = await _userManager.GetRolesAsync(user);
return Ok(new AuthResponseDto
{
IsSuccess = true,
Token = token,
UserName = user.UserName,
Email = user.Email,
Message = "Google login successful",
Roles = roles.ToList()
});
}
catch (Exception ex)
{
return BadRequest(new AuthResponseDto
{
IsSuccess = false,
Message = $"Authentication failed: {ex.Message}"
});
}
}
}
5. Data Transfer Objects (DTOs)
Create the necessary DTOs for handling requests and responses:
public class GoogleLoginDTO
{
public string IdToken { get; set; }
}
public class AuthResponseDto
{
public bool IsSuccess { get; set; }
public string Message { get; set; }
public string Token { get; set; }
public string UserName { get; set; }
public string Email { get; set; }
public List<string> Roles { get; set; }
}
6. Frontend Implementation
Here's a simple HTML page with JavaScript to implement Google Sign-In:
Create a wwwroot folder and root and add index.html
html<!DOCTYPE html>
<html>
<head>
<title>Google OAuth</title>
<script src="https://accounts.google.com/gsi/client" async defer></script>
</head>
<body>
<h1>Login with Google</h1>
<div id="g_id_onload"
data-client_id="YOUR_GOOGLE_CLIENT_ID"
data-callback="handleGoogleResponse"
data-auto_prompt="false">
</div>
<div class="g_id_signin"
data-type="standard"
data-size="large"
data-theme="outline"
data-text="sign_in_with"
data-shape="rectangular"
data-logo_alignment="left">
</div>
<script>
function handleGoogleResponse(response) {
console.log("Google response received");
// Send the ID token to your backend
fetch('/api/auth/google-login', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({ idToken: response.credential })
})
.then(response => response.json())
.then(data => {
console.log('Success:', data);
if (data.isSuccess) {
// Store the JWT token
localStorage.setItem('jwt', data.token);
// Redirect or update UI as needed
alert('Login successful!');
} else {
alert('Login failed: ' + data.message);
}
})
.catch(error => {
console.error('Error:', error);
alert('Login failed');
});
}
</script>
</body>
</html>
To use static files on your project add app.UseStaticFiles(); before app.run();
Github: https://github.com/sudipbhr/asp.net-web-api-auth