diff --git a/Marco.Pms.Model/Authentication/JwtSettings.cs b/Marco.Pms.Model/Authentication/JwtSettings.cs index d443b3a..6deec08 100644 --- a/Marco.Pms.Model/Authentication/JwtSettings.cs +++ b/Marco.Pms.Model/Authentication/JwtSettings.cs @@ -4,8 +4,14 @@ { public string? Key { get; set; } public string? Issuer { get; set; } - public string? Audience { get; set; } + public Audience? Audience { get; set; } public int ExpiresInMinutes { get; set; } public int RefreshTokenExpiresInDays { get; set; } } + + public class Audience + { + public string? WebApp { get; set; } + public string? MobileApp { get; set; } + } } diff --git a/Marco.Pms.Services/Program.cs b/Marco.Pms.Services/Program.cs index 17eb5c7..21a532c 100644 --- a/Marco.Pms.Services/Program.cs +++ b/Marco.Pms.Services/Program.cs @@ -160,7 +160,11 @@ if (jwtSettings != null && jwtSettings.Key != null) ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = jwtSettings.Issuer, - ValidAudience = jwtSettings.Audience, + ValidAudiences = new List + { + jwtSettings.Audience?.WebApp ?? "", + jwtSettings.Audience?.MobileApp ?? "" + }, IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(jwtSettings.Key)) }; diff --git a/Marco.Pms.Services/Service/RefreshTokenService.cs b/Marco.Pms.Services/Service/RefreshTokenService.cs index 018de68..d208122 100644 --- a/Marco.Pms.Services/Service/RefreshTokenService.cs +++ b/Marco.Pms.Services/Service/RefreshTokenService.cs @@ -15,18 +15,24 @@ namespace MarcoBMS.Services.Service private readonly ApplicationDbContext _context; private readonly IMemoryCache _cache; // For optional JWT blacklisting private readonly ILoggingService _logger; + private string Origin; - public RefreshTokenService(ApplicationDbContext context, IMemoryCache cache, ILoggingService logger) + public RefreshTokenService(ApplicationDbContext context, IMemoryCache cache, ILoggingService logger, IHttpContextAccessor httpContextAccessor) { _context = context; _cache = cache; _logger = logger; + Origin = httpContextAccessor.HttpContext.Request.Headers["Origin"].FirstOrDefault() ?? ""; } public string GenerateJwtToken(string username, Guid tenantId, JwtSettings _jwtSettings) { - + var audience = _jwtSettings.Audience.WebApp; + if (string.IsNullOrWhiteSpace(Origin)) + { + audience = _jwtSettings.Audience.MobileApp; + } // Custom claims var claims = new List { @@ -40,7 +46,7 @@ namespace MarcoBMS.Services.Service var token = new JwtSecurityToken( issuer: _jwtSettings.Issuer, - audience: _jwtSettings.Audience, + audience: audience, claims: claims, expires: DateTime.UtcNow.AddMinutes(_jwtSettings.ExpiresInMinutes), signingCredentials: creds); @@ -52,6 +58,11 @@ namespace MarcoBMS.Services.Service { try { + var audience = jwtSettings.Audience.WebApp; + if (string.IsNullOrWhiteSpace(Origin)) + { + audience = jwtSettings.Audience.MobileApp; + } var claims = new[] { new Claim(ClaimTypes.NameIdentifier, userId), @@ -67,7 +78,7 @@ namespace MarcoBMS.Services.Service Subject = new ClaimsIdentity(claims), Expires = DateTime.UtcNow.AddDays(jwtSettings.RefreshTokenExpiresInDays), Issuer = jwtSettings.Issuer, - Audience = jwtSettings.Audience, + Audience = audience, SigningCredentials = credentials }; @@ -102,7 +113,6 @@ namespace MarcoBMS.Services.Service { try { - var claims = new[] { new Claim(ClaimTypes.NameIdentifier, userId), @@ -117,7 +127,7 @@ namespace MarcoBMS.Services.Service { Subject = new ClaimsIdentity(claims), Issuer = jwtSettings.Issuer, - Audience = jwtSettings.Audience, + Audience = jwtSettings.Audience.MobileApp, SigningCredentials = creds // No 'Expires' means the token won't expire }; @@ -195,6 +205,11 @@ namespace MarcoBMS.Services.Service public ClaimsPrincipal ValidateToken(string token, JwtSettings jwtSettings) { + var audience = jwtSettings.Audience.WebApp; + if (string.IsNullOrWhiteSpace(Origin)) + { + audience = jwtSettings.Audience.MobileApp; + } var tokenHandler = new JwtSecurityTokenHandler(); var key = System.Text.Encoding.ASCII.GetBytes(jwtSettings.Key); @@ -205,7 +220,7 @@ namespace MarcoBMS.Services.Service ValidateIssuer = true, ValidIssuer = jwtSettings.Issuer, ValidateAudience = true, - ValidAudience = jwtSettings.Audience, + ValidAudience = audience, ValidateLifetime = false, // Disable lifetime validation (ignores expiration) ClockSkew = TimeSpan.Zero // Optional: Remove time skew buffer }; @@ -218,7 +233,7 @@ namespace MarcoBMS.Services.Service catch (Exception ex) { // Token is invalid - Console.WriteLine($"Token validation failed: {ex.Message}"); + _logger.LogError($"Token validation failed: {ex.Message}"); return null; } } diff --git a/Marco.Pms.Services/appsettings.Development.json b/Marco.Pms.Services/appsettings.Development.json index 1565018..81743a1 100644 --- a/Marco.Pms.Services/appsettings.Development.json +++ b/Marco.Pms.Services/appsettings.Development.json @@ -31,7 +31,10 @@ }, "Jwt": { "Issuer": "http://localhost:5246", - "Audience": "http://localhost:5246", + "Audience": { + "WebApp": "http://localhost:5246", + "MobileApp": "com.example.marcostage" // Use your actual application ID + }, "Key": "sworffishhkjfa9dnfdndfu33infnajfj", "ExpiresInMinutes": 60, "RefreshTokenExpiresInDays": 7 diff --git a/Marco.Pms.Services/appsettings.Production.json b/Marco.Pms.Services/appsettings.Production.json index 81aa998..91e0d15 100644 --- a/Marco.Pms.Services/appsettings.Production.json +++ b/Marco.Pms.Services/appsettings.Production.json @@ -6,7 +6,7 @@ }, "Environment": { "Name": "Production", - "Title": "" + "Title": "" }, "ConnectionStrings": { "DefaultConnectionString": "Server=147.93.98.152;User ID=devuser;Password=AppUser@123$;Database=MarcoBMS1" @@ -23,8 +23,11 @@ "ImagesBaseUrl": "https://app.marcoaiot.com" }, "Jwt": { - "Issuer": "https://app.marcoaiot.com", - "Audience": "https://app.marcoaiot.com", + "Issuer": "http://localhost:5246", + "Audience": { + "WebApp": "http://localhost:5246", + "MobileApp": "com.example.marcostage" // Use your actual application ID + }, "Key": "sworffishhkjfa9dnfdndfu33infnajfj", "ExpiresInMinutes": 60, "RefreshTokenExpiresInDays": 7