using Marco.Pms.DataAccess.Data; using Marco.Pms.Model.Authentication; using Marco.Pms.Model.Entitlements; using Microsoft.AspNetCore.Identity; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Caching.Memory; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json.Linq; using SharpCompress.Common; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Security.Cryptography; using System.Text; namespace MarcoBMS.Services.Service { public class RefreshTokenService { private readonly ApplicationDbContext _context; private readonly IMemoryCache _cache; // For optional JWT blacklisting public RefreshTokenService(ApplicationDbContext context, IMemoryCache cache) { _context = context; _cache = cache; } public string GenerateJwtToken(string username, string tenantId, JwtSettings _jwtSettings) { // Custom claims var claims = new List { new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()), new Claim(JwtRegisteredClaimNames.Sub, username), new Claim("TenantId", tenantId), // Add TenantId claim new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()) }; var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_jwtSettings.Key)); var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); var token = new JwtSecurityToken( issuer: _jwtSettings.Issuer, audience: _jwtSettings.Audience, claims: claims, expires: DateTime.UtcNow.AddMinutes(_jwtSettings.ExpiresInMinutes), signingCredentials: creds); return new JwtSecurityTokenHandler().WriteToken(token); } public async Task CreateRefreshToken(string userId, string tenantId, JwtSettings _jwtSettings) { try { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.UTF8.GetBytes(_jwtSettings.Key); var tokenDescriptor = new SecurityTokenDescriptor { Subject = new ClaimsIdentity(new[] { new Claim(ClaimTypes.NameIdentifier, userId), new Claim("TenantId", tenantId), // Add TenantId claim new Claim("token_type", "refresh") // Custom claim to differentiate refresh tokens }), Expires = DateTime.UtcNow.AddDays(7), // Refresh token valid for 7 days Issuer = _jwtSettings.Issuer, Audience = _jwtSettings.Audience, SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature) }; var token = tokenHandler.CreateToken(tokenDescriptor); string strToken = tokenHandler.WriteToken(token); var refreshToken = new RefreshToken { Token = strToken, UserId = userId, ExpiryDate = DateTime.UtcNow.AddDays(_jwtSettings.RefreshTokenExpiresInDays), IsRevoked = false }; // Check if the record exists var existingEntity = await _context.RefreshTokens.AnyAsync(c=>c.UserId == userId); if(existingEntity) { _context.RefreshTokens.Update(refreshToken); } else { _context.RefreshTokens.Add(refreshToken); } await _context.SaveChangesAsync(); return strToken; }catch(Exception ex) { throw; } } public async Task GetRefreshToken(string token) { return await _context.RefreshTokens.FirstOrDefaultAsync(rt => rt.Token == token && !rt.IsRevoked && !rt.IsUsed); } public async Task MarkRefreshTokenAsUsed(RefreshToken refreshToken) { refreshToken.IsUsed = true; _context.RefreshTokens.Update(refreshToken); await _context.SaveChangesAsync(); } public async Task RevokeRefreshToken(RefreshToken refreshToken) { refreshToken.IsRevoked = true; refreshToken.RevokedAt = DateTime.UtcNow; _context.RefreshTokens.Update(refreshToken); await _context.SaveChangesAsync(); } // Revoke refresh token public async Task RevokeRefreshTokenAsync(string refreshToken) { var token = await _context.RefreshTokens.FirstOrDefaultAsync(t => t.Token == refreshToken); if (token == null || token.IsRevoked || token.ExpiryDate <= DateTime.UtcNow) return false; token.IsRevoked = true; token.RevokedAt = DateTime.UtcNow; await _context.SaveChangesAsync(); return true; } // Optional: Blacklist JWT token public Task BlacklistJwtTokenAsync(string jwtToken) { // Store the JWT token in memory cache with its expiry var jwtExpiry = GetJwtExpiry(jwtToken); if (jwtExpiry.HasValue) { _cache.Set(jwtToken, true, jwtExpiry.Value - DateTime.UtcNow); } return Task.CompletedTask; } private DateTime? GetJwtExpiry(string token) { var handler = new JwtSecurityTokenHandler(); var jwtToken = handler.ReadToken(token) as JwtSecurityToken; return jwtToken?.ValidTo; } } }