using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Configuration; using Microsoft.IdentityModel.Tokens; using System; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using Hasher; public class JwtAuthenticationMiddleware { private readonly RequestDelegate _next; private readonly IConfiguration _configuration; private readonly ILogger _logger; public JwtAuthenticationMiddleware(RequestDelegate next, IConfiguration configuration, ILogger logger) { _next = next; _configuration = configuration; _logger = logger; } public async Task InvokeAsync(HttpContext context) { _logger.LogDebug($"Request cookies: {context.Request.Cookies}"); // Read the Authorization and UserFingerprint cookies context.Request.Cookies.TryGetValue("__Secure-Authorization", out string token); context.Request.Cookies.TryGetValue("__Secure-UserFingerprint", out string fingerprint); if (!string.IsNullOrEmpty(token) && !string.IsNullOrEmpty(fingerprint)) { // Validate the JWT and fingerprint var validationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = _configuration["JwtSettings:issuer"], ValidAudience = _configuration["JwtSettings:audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_configuration["JwtSettings:SecretKey"])), ClockSkew = TimeSpan.Zero }; try { var tokenHandler = new JwtSecurityTokenHandler(); tokenHandler.ValidateToken(token, validationParameters, out SecurityToken validatedToken); // Extract claims from the validated token var jwtToken = (JwtSecurityToken)validatedToken; var claims = jwtToken.Claims; // Validate the fingerprint var fingerprintClaim = claims.FirstOrDefault(c => c.Type == "Fingerprint"); if (fingerprintClaim != null) { var hashedFingerprint = fingerprintClaim.Value; _logger.LogDebug($"HashedFp: {hashedFingerprint} | Cookie Fp: {fingerprintClaim}"); // Verify the fingerprint here, based on how you generate the fingerprint hash // If the fingerprint is valid, set the User property with the claims if (FingerPrinter.ValidateFingerpint(fingerprint,hashedFingerprint)) { // Record the claims so that they can be accessed by controllers context.User = new ClaimsPrincipal(new ClaimsIdentity(claims.Select(c => c.Type == "Auth" ? new Claim(ClaimTypes.Role, c.Value) : c), "Jwt")); } } // Validate signed headers string payloadSig = context.Request.Headers["X-Payload-Signature"]; // Read the request body content string payload; using (var streamReader = new StreamReader(context.Request.Body)) { payload = await streamReader.ReadToEndAsync(); } string clientPublicKey = claims.FirstOrDefault(c => c.Type == "ClientPublicKey").Value; _logger.LogDebug($"PayloadSig: {payloadSig} | payload: {payload} | Key: {clientPublicKey}"); //FIXME This could use some testing try { bool validSig = KeyHelper.validateSignature(payloadSig, payload, clientPublicKey); _logger.LogDebug($"Valid sig!"); } catch { _logger.LogDebug($"INVALID sig!"); } } catch (SecurityTokenValidationException) { //FIXME Log the validation error or perform necessary actions _logger.LogWarning("Invlaid JWT Exception!"); } } // Call the next middleware in the pipeline await _next(context); } }