This commit is contained in:
72
BimAI.API/Services/GoogleAuthService.cs
Normal file
72
BimAI.API/Services/GoogleAuthService.cs
Normal file
@@ -0,0 +1,72 @@
|
||||
using BimAI.Domain.Entities;
|
||||
using BimAI.Infrastructure.Data;
|
||||
using Google.Apis.Auth;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
|
||||
namespace BimAI.API.Services;
|
||||
|
||||
public class GoogleAuthService(BimAIDbContext context, IConfiguration configuration, ILogger<GoogleAuthService> logger)
|
||||
{
|
||||
private readonly BimAIDbContext _context = context;
|
||||
private readonly IConfiguration _configuration = configuration;
|
||||
private readonly ILogger<GoogleAuthService> _logger = logger;
|
||||
|
||||
public async Task<(bool IsValid, User? user, string? error)> ValidateGoogleTokenAsync(string idToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
var clientId = _configuration["GoogleAuth:ClientId"];
|
||||
if (string.IsNullOrEmpty(clientId))
|
||||
{
|
||||
_logger.LogError("Google Auth Client Id is not configured");
|
||||
return (false, null, "Google Auth Client Id is not configured");
|
||||
}
|
||||
|
||||
var payload = await GoogleJsonWebSignature.ValidateAsync(idToken,
|
||||
new GoogleJsonWebSignature.ValidationSettings
|
||||
{
|
||||
Audience = new[] { clientId }
|
||||
});
|
||||
|
||||
_logger.LogInformation("Google token validated for user: {Email}", payload.Email);
|
||||
|
||||
var user = await _context.Users
|
||||
.FirstOrDefaultAsync(x => x.GoogleId == payload.Subject || x.Email == payload.Email);
|
||||
|
||||
if (user == null)
|
||||
{
|
||||
_logger.LogError("User not found in BimAI database: {Email}", payload.Email);
|
||||
return (false, null, "User not found in BimAI database");
|
||||
}
|
||||
|
||||
if (!user.IsActive)
|
||||
{
|
||||
_logger.LogError("User is not active: {Email}", payload.Email);
|
||||
return (false, null, "User is not active");
|
||||
}
|
||||
|
||||
user.LastLoginAt = DateTime.UtcNow;
|
||||
user.FullName = payload.Name;
|
||||
|
||||
if (user.GoogleId != payload.Subject)
|
||||
{
|
||||
user.GoogleId = payload.Subject;
|
||||
}
|
||||
|
||||
await _context.SaveChangesAsync();
|
||||
|
||||
_logger.LogInformation("User logged in: {Email}", payload.Email);
|
||||
|
||||
return (true, user, null);
|
||||
}
|
||||
catch (InvalidJwtException ex)
|
||||
{
|
||||
_logger.LogError(ex, "Invalid JWT token");
|
||||
return (false, null, "Invalid JWT token");
|
||||
} catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error validating Google token");
|
||||
return (false, null, "Error validating Google token");
|
||||
}
|
||||
}
|
||||
}
|
||||
86
BimAI.API/Services/JwtTokenService.cs
Normal file
86
BimAI.API/Services/JwtTokenService.cs
Normal file
@@ -0,0 +1,86 @@
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using BimAI.Domain.Entities;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
|
||||
namespace BimAI.API.Services;
|
||||
|
||||
public class JwtTokenService(IConfiguration configuration, ILogger<JwtTokenService> logger)
|
||||
{
|
||||
private readonly IConfiguration _configuration = configuration;
|
||||
private readonly ILogger<JwtTokenService> _logger = logger;
|
||||
|
||||
public string GenerateToken(User user)
|
||||
{
|
||||
var jwtSettings = _configuration.GetSection("JwtSettings");
|
||||
var securityKey = jwtSettings["SecurityKey"];
|
||||
var issuer = jwtSettings["Issuer"];
|
||||
var audience = jwtSettings["Audience"];
|
||||
var expiryDays = int.Parse(jwtSettings["ExpiryDays"] ?? "7");
|
||||
|
||||
var claims = new[]
|
||||
{
|
||||
new Claim(ClaimTypes.NameIdentifier, user.Id.ToString()),
|
||||
new Claim(ClaimTypes.Email, user.Email),
|
||||
new Claim(ClaimTypes.Name, user.FullName),
|
||||
new Claim("google_id", user.GoogleId),
|
||||
new Claim("is_active", user.IsActive.ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
|
||||
new Claim(JwtRegisteredClaimNames.Iat, new DateTimeOffset(DateTime.UtcNow).ToUnixTimeSeconds().ToString(),
|
||||
ClaimValueTypes.Integer64)
|
||||
};
|
||||
|
||||
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(securityKey));
|
||||
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: issuer,
|
||||
audience: audience,
|
||||
claims: claims,
|
||||
expires: DateTime.UtcNow.AddDays(expiryDays),
|
||||
signingCredentials: creds
|
||||
);
|
||||
|
||||
var tokenString = new JwtSecurityTokenHandler().WriteToken(token);
|
||||
|
||||
_logger.LogInformation("Generated JWT token for user: {Email}", user.Email);
|
||||
|
||||
return tokenString;
|
||||
}
|
||||
|
||||
public ClaimsPrincipal? ValidateToken(string token)
|
||||
{
|
||||
try
|
||||
{
|
||||
var jwtSettings = _configuration.GetSection("JwtSettings");
|
||||
var secretKey = jwtSettings["SecretKey"];
|
||||
var issuer = jwtSettings["Issuer"];
|
||||
var audience = jwtSettings["Audience"];
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var key = Encoding.UTF8.GetBytes(secretKey);
|
||||
|
||||
var validationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuer = true,
|
||||
ValidateAudience = true,
|
||||
ValidateLifetime = true,
|
||||
ValidateIssuerSigningKey = true,
|
||||
ValidIssuer = issuer,
|
||||
ValidAudience = audience,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(key),
|
||||
ClockSkew = TimeSpan.Zero
|
||||
};
|
||||
|
||||
var principal = tokenHandler.ValidateToken(token, validationParameters, out _);
|
||||
return principal;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger.LogError(ex, "Error validating JWT token");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user